mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
9
drivers/parport/BUGS-parport
Normal file
9
drivers/parport/BUGS-parport
Normal file
|
@ -0,0 +1,9 @@
|
|||
Currently known (or at least suspected) bugs in parport:
|
||||
|
||||
o lp doesn't allow you to read status while printing is in progress (is
|
||||
this still true?).
|
||||
|
||||
o parport_pc_ecp_read_block_pio() is broken. parport will revert to the
|
||||
software-driven mode in ieee1284_ops.c
|
||||
|
||||
See <URL:http://people.redhat.com/twaugh/parport/>.
|
165
drivers/parport/Kconfig
Normal file
165
drivers/parport/Kconfig
Normal file
|
@ -0,0 +1,165 @@
|
|||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see Documentation/kbuild/kconfig-language.txt.
|
||||
#
|
||||
# Parport configuration.
|
||||
#
|
||||
|
||||
config ARCH_MIGHT_HAVE_PC_PARPORT
|
||||
bool
|
||||
help
|
||||
Select this config option from the architecture Kconfig if
|
||||
the architecture might have PC parallel port hardware.
|
||||
|
||||
menuconfig PARPORT
|
||||
tristate "Parallel port support"
|
||||
depends on HAS_IOMEM
|
||||
---help---
|
||||
If you want to use devices connected to your machine's parallel port
|
||||
(the connector at the computer with 25 holes), e.g. printer, ZIP
|
||||
drive, PLIP link (Parallel Line Internet Protocol is mainly used to
|
||||
create a mini network by connecting the parallel ports of two local
|
||||
machines) etc., then you need to say Y here; please read
|
||||
<file:Documentation/parport.txt> and
|
||||
<file:drivers/parport/BUGS-parport>.
|
||||
|
||||
For extensive information about drivers for many devices attaching
|
||||
to the parallel port see <http://www.torque.net/linux-pp.html> on
|
||||
the WWW.
|
||||
|
||||
It is possible to share a single parallel port among several devices
|
||||
and it is safe to compile all the corresponding drivers into the
|
||||
kernel. To compile parallel port support as a module, choose M here:
|
||||
the module will be called parport.
|
||||
If you have more than one parallel port and want to specify which
|
||||
port and IRQ to be used by this driver at module load time, take a
|
||||
look at <file:Documentation/parport.txt>.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
if PARPORT
|
||||
|
||||
config PARPORT_PC
|
||||
tristate "PC-style hardware"
|
||||
depends on ARCH_MIGHT_HAVE_PC_PARPORT
|
||||
help
|
||||
You should say Y here if you have a PC-style parallel port. All
|
||||
IBM PC compatible computers and some Alphas have PC-style
|
||||
parallel ports. PA-RISC owners should only say Y here if they
|
||||
have a SuperIO parallel port.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called parport_pc.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config PARPORT_SERIAL
|
||||
tristate "Multi-IO cards (parallel and serial)"
|
||||
depends on SERIAL_8250_PCI && PARPORT_PC && PCI
|
||||
help
|
||||
This adds support for multi-IO PCI cards that have parallel and
|
||||
serial ports. You should say Y or M here. If you say M, the module
|
||||
will be called parport_serial.
|
||||
|
||||
config PARPORT_PC_FIFO
|
||||
bool "Use FIFO/DMA if available"
|
||||
depends on PARPORT_PC
|
||||
help
|
||||
Many parallel port chipsets provide hardware that can speed up
|
||||
printing. Say Y here if you want to take advantage of that.
|
||||
|
||||
As well as actually having a FIFO, or DMA capability, the kernel
|
||||
will need to know which IRQ the parallel port has. By default,
|
||||
parallel port interrupts will not be used, and so neither will the
|
||||
FIFO. See <file:Documentation/parport.txt> to find out how to
|
||||
specify which IRQ/DMA to use.
|
||||
|
||||
config PARPORT_PC_SUPERIO
|
||||
bool "SuperIO chipset support"
|
||||
depends on PARPORT_PC && !PARISC
|
||||
help
|
||||
Saying Y here enables some probes for Super-IO chipsets in order to
|
||||
find out things like base addresses, IRQ lines and DMA channels. It
|
||||
is safe to say N.
|
||||
|
||||
config PARPORT_PC_PCMCIA
|
||||
tristate "Support for PCMCIA management for PC-style ports"
|
||||
depends on PCMCIA && PARPORT_PC
|
||||
help
|
||||
Say Y here if you need PCMCIA support for your PC-style parallel
|
||||
ports. If unsure, say N.
|
||||
|
||||
config PARPORT_IP32
|
||||
tristate "SGI IP32 builtin port"
|
||||
depends on SGI_IP32
|
||||
select PARPORT_NOT_PC
|
||||
help
|
||||
Say Y here if you need support for the parallel port on
|
||||
SGI O2 machines. This code is also available as a module (say M),
|
||||
called parport_ip32. If in doubt, saying N is the safe plan.
|
||||
|
||||
config PARPORT_AMIGA
|
||||
tristate "Amiga builtin port"
|
||||
depends on AMIGA
|
||||
select PARPORT_NOT_PC
|
||||
help
|
||||
Say Y here if you need support for the parallel port hardware on
|
||||
Amiga machines. This code is also available as a module (say M),
|
||||
called parport_amiga. If in doubt, saying N is the safe plan.
|
||||
|
||||
config PARPORT_MFC3
|
||||
tristate "Multiface III parallel port"
|
||||
depends on ZORRO
|
||||
select PARPORT_NOT_PC
|
||||
help
|
||||
Say Y here if you need parallel port support for the MFC3 card.
|
||||
This code is also available as a module (say M), called
|
||||
parport_mfc3. If in doubt, saying N is the safe plan.
|
||||
|
||||
config PARPORT_ATARI
|
||||
tristate "Atari hardware"
|
||||
depends on ATARI
|
||||
select PARPORT_NOT_PC
|
||||
help
|
||||
Say Y here if you need support for the parallel port hardware on
|
||||
Atari machines. This code is also available as a module (say M),
|
||||
called parport_atari. If in doubt, saying N is the safe plan.
|
||||
|
||||
config PARPORT_GSC
|
||||
tristate
|
||||
default GSC
|
||||
select PARPORT_NOT_PC
|
||||
|
||||
config PARPORT_SUNBPP
|
||||
tristate "Sparc hardware"
|
||||
depends on SBUS
|
||||
select PARPORT_NOT_PC
|
||||
help
|
||||
This driver provides support for the bidirectional parallel port
|
||||
found on many Sun machines. Note that many of the newer Ultras
|
||||
actually have pc style hardware instead.
|
||||
|
||||
config PARPORT_AX88796
|
||||
tristate "AX88796 Parallel Port"
|
||||
select PARPORT_NOT_PC
|
||||
help
|
||||
Say Y here if you need support for the parallel port hardware on
|
||||
the AX88796 network controller chip. This code is also available
|
||||
as a module (say M), called parport_ax88796.
|
||||
|
||||
The driver is not dependent on the AX88796 network driver, and
|
||||
should not interfere with the networking functions of the chip.
|
||||
|
||||
config PARPORT_1284
|
||||
bool "IEEE 1284 transfer modes"
|
||||
help
|
||||
If you have a printer that supports status readback or device ID, or
|
||||
want to use a device that uses enhanced parallel port transfer modes
|
||||
such as EPP and ECP, say Y here to enable advanced IEEE 1284
|
||||
transfer modes. Also say Y if you want device ID information to
|
||||
appear in /proc/sys/dev/parport/*/autoprobe*. It is safe to say N.
|
||||
|
||||
config PARPORT_NOT_PC
|
||||
bool
|
||||
|
||||
endif # PARPORT
|
21
drivers/parport/Makefile
Normal file
21
drivers/parport/Makefile
Normal file
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
# Makefile for the kernel Parallel port device drivers.
|
||||
#
|
||||
|
||||
parport-objs := share.o ieee1284.o ieee1284_ops.o procfs.o
|
||||
|
||||
ifeq ($(CONFIG_PARPORT_1284),y)
|
||||
parport-objs += daisy.o probe.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_PARPORT) += parport.o
|
||||
obj-$(CONFIG_PARPORT_PC) += parport_pc.o
|
||||
obj-$(CONFIG_PARPORT_SERIAL) += parport_serial.o
|
||||
obj-$(CONFIG_PARPORT_PC_PCMCIA) += parport_cs.o
|
||||
obj-$(CONFIG_PARPORT_AMIGA) += parport_amiga.o
|
||||
obj-$(CONFIG_PARPORT_MFC3) += parport_mfc3.o
|
||||
obj-$(CONFIG_PARPORT_ATARI) += parport_atari.o
|
||||
obj-$(CONFIG_PARPORT_SUNBPP) += parport_sunbpp.o
|
||||
obj-$(CONFIG_PARPORT_GSC) += parport_gsc.o
|
||||
obj-$(CONFIG_PARPORT_AX88796) += parport_ax88796.o
|
||||
obj-$(CONFIG_PARPORT_IP32) += parport_ip32.o
|
20
drivers/parport/TODO-parport
Normal file
20
drivers/parport/TODO-parport
Normal file
|
@ -0,0 +1,20 @@
|
|||
Things to be done.
|
||||
|
||||
0. Fix the bugs (see BUGS-parport).
|
||||
|
||||
1. Proper documentation.
|
||||
|
||||
2. A better lp.c:
|
||||
|
||||
a) ECP support would be nice. This can only work if both the port and
|
||||
the printer support it.
|
||||
|
||||
b) Handle status readback automatically. IEEE1284 printers can post status
|
||||
bits when they have something to say. We should read out and deal
|
||||
with (maybe just log) whatever the printer wants to tell the world.
|
||||
|
||||
3. Support more hardware (eg m68k, Sun bpp).
|
||||
|
||||
4. A better PLIP (make use of bidirectional/ECP/EPP ports).
|
||||
|
||||
See <URL:http://people.redhat.com/twaugh/parport/>.
|
482
drivers/parport/daisy.c
Normal file
482
drivers/parport/daisy.c
Normal file
|
@ -0,0 +1,482 @@
|
|||
/*
|
||||
* IEEE 1284.3 Parallel port daisy chain and multiplexor code
|
||||
*
|
||||
* Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* ??-12-1998: Initial implementation.
|
||||
* 31-01-1999: Make port-cloning transparent.
|
||||
* 13-02-1999: Move DeviceID technique from parport_probe.
|
||||
* 13-03-1999: Get DeviceID from non-IEEE 1284.3 devices too.
|
||||
* 22-02-2000: Count devices that are actually detected.
|
||||
*
|
||||
* Any part of this program may be used in documents licensed under
|
||||
* the GNU Free Documentation License, Version 1.1 or any later version
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <asm/current.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DPRINTK(stuff...) printk(stuff)
|
||||
#else
|
||||
#define DPRINTK(stuff...)
|
||||
#endif
|
||||
|
||||
static struct daisydev {
|
||||
struct daisydev *next;
|
||||
struct parport *port;
|
||||
int daisy;
|
||||
int devnum;
|
||||
} *topology = NULL;
|
||||
static DEFINE_SPINLOCK(topology_lock);
|
||||
|
||||
static int numdevs = 0;
|
||||
|
||||
/* Forward-declaration of lower-level functions. */
|
||||
static int mux_present(struct parport *port);
|
||||
static int num_mux_ports(struct parport *port);
|
||||
static int select_port(struct parport *port);
|
||||
static int assign_addrs(struct parport *port);
|
||||
|
||||
/* Add a device to the discovered topology. */
|
||||
static void add_dev(int devnum, struct parport *port, int daisy)
|
||||
{
|
||||
struct daisydev *newdev, **p;
|
||||
newdev = kmalloc(sizeof(struct daisydev), GFP_KERNEL);
|
||||
if (newdev) {
|
||||
newdev->port = port;
|
||||
newdev->daisy = daisy;
|
||||
newdev->devnum = devnum;
|
||||
spin_lock(&topology_lock);
|
||||
for (p = &topology; *p && (*p)->devnum<devnum; p = &(*p)->next)
|
||||
;
|
||||
newdev->next = *p;
|
||||
*p = newdev;
|
||||
spin_unlock(&topology_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clone a parport (actually, make an alias). */
|
||||
static struct parport *clone_parport(struct parport *real, int muxport)
|
||||
{
|
||||
struct parport *extra = parport_register_port(real->base,
|
||||
real->irq,
|
||||
real->dma,
|
||||
real->ops);
|
||||
if (extra) {
|
||||
extra->portnum = real->portnum;
|
||||
extra->physport = real;
|
||||
extra->muxport = muxport;
|
||||
real->slaves[muxport-1] = extra;
|
||||
}
|
||||
|
||||
return extra;
|
||||
}
|
||||
|
||||
/* Discover the IEEE1284.3 topology on a port -- muxes and daisy chains.
|
||||
* Return value is number of devices actually detected. */
|
||||
int parport_daisy_init(struct parport *port)
|
||||
{
|
||||
int detected = 0;
|
||||
char *deviceid;
|
||||
static const char *th[] = { /*0*/"th", "st", "nd", "rd", "th" };
|
||||
int num_ports;
|
||||
int i;
|
||||
int last_try = 0;
|
||||
|
||||
again:
|
||||
/* Because this is called before any other devices exist,
|
||||
* we don't have to claim exclusive access. */
|
||||
|
||||
/* If mux present on normal port, need to create new
|
||||
* parports for each extra port. */
|
||||
if (port->muxport < 0 && mux_present(port) &&
|
||||
/* don't be fooled: a mux must have 2 or 4 ports. */
|
||||
((num_ports = num_mux_ports(port)) == 2 || num_ports == 4)) {
|
||||
/* Leave original as port zero. */
|
||||
port->muxport = 0;
|
||||
printk(KERN_INFO
|
||||
"%s: 1st (default) port of %d-way multiplexor\n",
|
||||
port->name, num_ports);
|
||||
for (i = 1; i < num_ports; i++) {
|
||||
/* Clone the port. */
|
||||
struct parport *extra = clone_parport(port, i);
|
||||
if (!extra) {
|
||||
if (signal_pending(current))
|
||||
break;
|
||||
|
||||
schedule();
|
||||
continue;
|
||||
}
|
||||
|
||||
printk(KERN_INFO
|
||||
"%s: %d%s port of %d-way multiplexor on %s\n",
|
||||
extra->name, i + 1, th[i + 1], num_ports,
|
||||
port->name);
|
||||
|
||||
/* Analyse that port too. We won't recurse
|
||||
forever because of the 'port->muxport < 0'
|
||||
test above. */
|
||||
parport_daisy_init(extra);
|
||||
}
|
||||
}
|
||||
|
||||
if (port->muxport >= 0)
|
||||
select_port(port);
|
||||
|
||||
parport_daisy_deselect_all(port);
|
||||
detected += assign_addrs(port);
|
||||
|
||||
/* Count the potential legacy device at the end. */
|
||||
add_dev(numdevs++, port, -1);
|
||||
|
||||
/* Find out the legacy device's IEEE 1284 device ID. */
|
||||
deviceid = kmalloc(1024, GFP_KERNEL);
|
||||
if (deviceid) {
|
||||
if (parport_device_id(numdevs - 1, deviceid, 1024) > 2)
|
||||
detected++;
|
||||
|
||||
kfree(deviceid);
|
||||
}
|
||||
|
||||
if (!detected && !last_try) {
|
||||
/* No devices were detected. Perhaps they are in some
|
||||
funny state; let's try to reset them and see if
|
||||
they wake up. */
|
||||
parport_daisy_fini(port);
|
||||
parport_write_control(port, PARPORT_CONTROL_SELECT);
|
||||
udelay(50);
|
||||
parport_write_control(port,
|
||||
PARPORT_CONTROL_SELECT |
|
||||
PARPORT_CONTROL_INIT);
|
||||
udelay(50);
|
||||
last_try = 1;
|
||||
goto again;
|
||||
}
|
||||
|
||||
return detected;
|
||||
}
|
||||
|
||||
/* Forget about devices on a physical port. */
|
||||
void parport_daisy_fini(struct parport *port)
|
||||
{
|
||||
struct daisydev **p;
|
||||
|
||||
spin_lock(&topology_lock);
|
||||
p = &topology;
|
||||
while (*p) {
|
||||
struct daisydev *dev = *p;
|
||||
if (dev->port != port) {
|
||||
p = &dev->next;
|
||||
continue;
|
||||
}
|
||||
*p = dev->next;
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
/* Gaps in the numbering could be handled better. How should
|
||||
someone enumerate through all IEEE1284.3 devices in the
|
||||
topology?. */
|
||||
if (!topology) numdevs = 0;
|
||||
spin_unlock(&topology_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* parport_open - find a device by canonical device number
|
||||
* @devnum: canonical device number
|
||||
* @name: name to associate with the device
|
||||
*
|
||||
* This function is similar to parport_register_device(), except
|
||||
* that it locates a device by its number rather than by the port
|
||||
* it is attached to.
|
||||
*
|
||||
* All parameters except for @devnum are the same as for
|
||||
* parport_register_device(). The return value is the same as
|
||||
* for parport_register_device().
|
||||
**/
|
||||
|
||||
struct pardevice *parport_open(int devnum, const char *name)
|
||||
{
|
||||
struct daisydev *p = topology;
|
||||
struct parport *port;
|
||||
struct pardevice *dev;
|
||||
int daisy;
|
||||
|
||||
spin_lock(&topology_lock);
|
||||
while (p && p->devnum != devnum)
|
||||
p = p->next;
|
||||
|
||||
if (!p) {
|
||||
spin_unlock(&topology_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
daisy = p->daisy;
|
||||
port = parport_get_port(p->port);
|
||||
spin_unlock(&topology_lock);
|
||||
|
||||
dev = parport_register_device(port, name, NULL, NULL, NULL, 0, NULL);
|
||||
parport_put_port(port);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
dev->daisy = daisy;
|
||||
|
||||
/* Check that there really is a device to select. */
|
||||
if (daisy >= 0) {
|
||||
int selected;
|
||||
parport_claim_or_block(dev);
|
||||
selected = port->daisy;
|
||||
parport_release(dev);
|
||||
|
||||
if (selected != daisy) {
|
||||
/* No corresponding device. */
|
||||
parport_unregister_device(dev);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* parport_close - close a device opened with parport_open()
|
||||
* @dev: device to close
|
||||
*
|
||||
* This is to parport_open() as parport_unregister_device() is to
|
||||
* parport_register_device().
|
||||
**/
|
||||
|
||||
void parport_close(struct pardevice *dev)
|
||||
{
|
||||
parport_unregister_device(dev);
|
||||
}
|
||||
|
||||
/* Send a daisy-chain-style CPP command packet. */
|
||||
static int cpp_daisy(struct parport *port, int cmd)
|
||||
{
|
||||
unsigned char s;
|
||||
|
||||
parport_data_forward(port);
|
||||
parport_write_data(port, 0xaa); udelay(2);
|
||||
parport_write_data(port, 0x55); udelay(2);
|
||||
parport_write_data(port, 0x00); udelay(2);
|
||||
parport_write_data(port, 0xff); udelay(2);
|
||||
s = parport_read_status(port) & (PARPORT_STATUS_BUSY
|
||||
| PARPORT_STATUS_PAPEROUT
|
||||
| PARPORT_STATUS_SELECT
|
||||
| PARPORT_STATUS_ERROR);
|
||||
if (s != (PARPORT_STATUS_BUSY
|
||||
| PARPORT_STATUS_PAPEROUT
|
||||
| PARPORT_STATUS_SELECT
|
||||
| PARPORT_STATUS_ERROR)) {
|
||||
DPRINTK(KERN_DEBUG "%s: cpp_daisy: aa5500ff(%02x)\n",
|
||||
port->name, s);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
parport_write_data(port, 0x87); udelay(2);
|
||||
s = parport_read_status(port) & (PARPORT_STATUS_BUSY
|
||||
| PARPORT_STATUS_PAPEROUT
|
||||
| PARPORT_STATUS_SELECT
|
||||
| PARPORT_STATUS_ERROR);
|
||||
if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) {
|
||||
DPRINTK(KERN_DEBUG "%s: cpp_daisy: aa5500ff87(%02x)\n",
|
||||
port->name, s);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
parport_write_data(port, 0x78); udelay(2);
|
||||
parport_write_data(port, cmd); udelay(2);
|
||||
parport_frob_control(port,
|
||||
PARPORT_CONTROL_STROBE,
|
||||
PARPORT_CONTROL_STROBE);
|
||||
udelay(1);
|
||||
s = parport_read_status(port);
|
||||
parport_frob_control(port, PARPORT_CONTROL_STROBE, 0);
|
||||
udelay(1);
|
||||
parport_write_data(port, 0xff); udelay(2);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Send a mux-style CPP command packet. */
|
||||
static int cpp_mux(struct parport *port, int cmd)
|
||||
{
|
||||
unsigned char s;
|
||||
int rc;
|
||||
|
||||
parport_data_forward(port);
|
||||
parport_write_data(port, 0xaa); udelay(2);
|
||||
parport_write_data(port, 0x55); udelay(2);
|
||||
parport_write_data(port, 0xf0); udelay(2);
|
||||
parport_write_data(port, 0x0f); udelay(2);
|
||||
parport_write_data(port, 0x52); udelay(2);
|
||||
parport_write_data(port, 0xad); udelay(2);
|
||||
parport_write_data(port, cmd); udelay(2);
|
||||
|
||||
s = parport_read_status(port);
|
||||
if (!(s & PARPORT_STATUS_ACK)) {
|
||||
DPRINTK(KERN_DEBUG "%s: cpp_mux: aa55f00f52ad%02x(%02x)\n",
|
||||
port->name, cmd, s);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rc = (((s & PARPORT_STATUS_SELECT ? 1 : 0) << 0) |
|
||||
((s & PARPORT_STATUS_PAPEROUT ? 1 : 0) << 1) |
|
||||
((s & PARPORT_STATUS_BUSY ? 0 : 1) << 2) |
|
||||
((s & PARPORT_STATUS_ERROR ? 0 : 1) << 3));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void parport_daisy_deselect_all(struct parport *port)
|
||||
{
|
||||
cpp_daisy(port, 0x30);
|
||||
}
|
||||
|
||||
int parport_daisy_select(struct parport *port, int daisy, int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
// For these modes we should switch to EPP mode:
|
||||
case IEEE1284_MODE_EPP:
|
||||
case IEEE1284_MODE_EPPSL:
|
||||
case IEEE1284_MODE_EPPSWE:
|
||||
return !(cpp_daisy(port, 0x20 + daisy) &
|
||||
PARPORT_STATUS_ERROR);
|
||||
|
||||
// For these modes we should switch to ECP mode:
|
||||
case IEEE1284_MODE_ECP:
|
||||
case IEEE1284_MODE_ECPRLE:
|
||||
case IEEE1284_MODE_ECPSWE:
|
||||
return !(cpp_daisy(port, 0xd0 + daisy) &
|
||||
PARPORT_STATUS_ERROR);
|
||||
|
||||
// Nothing was told for BECP in Daisy chain specification.
|
||||
// May be it's wise to use ECP?
|
||||
case IEEE1284_MODE_BECP:
|
||||
// Others use compat mode
|
||||
case IEEE1284_MODE_NIBBLE:
|
||||
case IEEE1284_MODE_BYTE:
|
||||
case IEEE1284_MODE_COMPAT:
|
||||
default:
|
||||
return !(cpp_daisy(port, 0xe0 + daisy) &
|
||||
PARPORT_STATUS_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static int mux_present(struct parport *port)
|
||||
{
|
||||
return cpp_mux(port, 0x51) == 3;
|
||||
}
|
||||
|
||||
static int num_mux_ports(struct parport *port)
|
||||
{
|
||||
return cpp_mux(port, 0x58);
|
||||
}
|
||||
|
||||
static int select_port(struct parport *port)
|
||||
{
|
||||
int muxport = port->muxport;
|
||||
return cpp_mux(port, 0x60 + muxport) == muxport;
|
||||
}
|
||||
|
||||
static int assign_addrs(struct parport *port)
|
||||
{
|
||||
unsigned char s;
|
||||
unsigned char daisy;
|
||||
int thisdev = numdevs;
|
||||
int detected;
|
||||
char *deviceid;
|
||||
|
||||
parport_data_forward(port);
|
||||
parport_write_data(port, 0xaa); udelay(2);
|
||||
parport_write_data(port, 0x55); udelay(2);
|
||||
parport_write_data(port, 0x00); udelay(2);
|
||||
parport_write_data(port, 0xff); udelay(2);
|
||||
s = parport_read_status(port) & (PARPORT_STATUS_BUSY
|
||||
| PARPORT_STATUS_PAPEROUT
|
||||
| PARPORT_STATUS_SELECT
|
||||
| PARPORT_STATUS_ERROR);
|
||||
if (s != (PARPORT_STATUS_BUSY
|
||||
| PARPORT_STATUS_PAPEROUT
|
||||
| PARPORT_STATUS_SELECT
|
||||
| PARPORT_STATUS_ERROR)) {
|
||||
DPRINTK(KERN_DEBUG "%s: assign_addrs: aa5500ff(%02x)\n",
|
||||
port->name, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
parport_write_data(port, 0x87); udelay(2);
|
||||
s = parport_read_status(port) & (PARPORT_STATUS_BUSY
|
||||
| PARPORT_STATUS_PAPEROUT
|
||||
| PARPORT_STATUS_SELECT
|
||||
| PARPORT_STATUS_ERROR);
|
||||
if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) {
|
||||
DPRINTK(KERN_DEBUG "%s: assign_addrs: aa5500ff87(%02x)\n",
|
||||
port->name, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
parport_write_data(port, 0x78); udelay(2);
|
||||
s = parport_read_status(port);
|
||||
|
||||
for (daisy = 0;
|
||||
(s & (PARPORT_STATUS_PAPEROUT|PARPORT_STATUS_SELECT))
|
||||
== (PARPORT_STATUS_PAPEROUT|PARPORT_STATUS_SELECT)
|
||||
&& daisy < 4;
|
||||
++daisy) {
|
||||
parport_write_data(port, daisy);
|
||||
udelay(2);
|
||||
parport_frob_control(port,
|
||||
PARPORT_CONTROL_STROBE,
|
||||
PARPORT_CONTROL_STROBE);
|
||||
udelay(1);
|
||||
parport_frob_control(port, PARPORT_CONTROL_STROBE, 0);
|
||||
udelay(1);
|
||||
|
||||
add_dev(numdevs++, port, daisy);
|
||||
|
||||
/* See if this device thought it was the last in the
|
||||
* chain. */
|
||||
if (!(s & PARPORT_STATUS_BUSY))
|
||||
break;
|
||||
|
||||
/* We are seeing pass through status now. We see
|
||||
last_dev from next device or if last_dev does not
|
||||
work status lines from some non-daisy chain
|
||||
device. */
|
||||
s = parport_read_status(port);
|
||||
}
|
||||
|
||||
parport_write_data(port, 0xff); udelay(2);
|
||||
detected = numdevs - thisdev;
|
||||
DPRINTK(KERN_DEBUG "%s: Found %d daisy-chained devices\n", port->name,
|
||||
detected);
|
||||
|
||||
/* Ask the new devices to introduce themselves. */
|
||||
deviceid = kmalloc(1024, GFP_KERNEL);
|
||||
if (!deviceid) return 0;
|
||||
|
||||
for (daisy = 0; thisdev < numdevs; thisdev++, daisy++)
|
||||
parport_device_id(thisdev, deviceid, 1024);
|
||||
|
||||
kfree(deviceid);
|
||||
return detected;
|
||||
}
|
817
drivers/parport/ieee1284.c
Normal file
817
drivers/parport/ieee1284.c
Normal file
|
@ -0,0 +1,817 @@
|
|||
/*
|
||||
* IEEE-1284 implementation for parport.
|
||||
*
|
||||
* Authors: Phil Blundell <philb@gnu.org>
|
||||
* Carsten Gross <carsten@sol.wohnheim.uni-ulm.de>
|
||||
* Jose Renau <renau@acm.org>
|
||||
* Tim Waugh <tim@cyberelk.demon.co.uk> (largely rewritten)
|
||||
*
|
||||
* This file is responsible for IEEE 1284 negotiation, and for handing
|
||||
* read/write requests to low-level drivers.
|
||||
*
|
||||
* Any part of this program may be used in documents licensed under
|
||||
* the GNU Free Documentation License, Version 1.1 or any later version
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Various hacks, Fred Barnes <frmb2@ukc.ac.uk>, 04/2000
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#undef DEBUG /* undef me for production */
|
||||
|
||||
#ifdef CONFIG_LP_CONSOLE
|
||||
#undef DEBUG /* Don't want a garbled console */
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DPRINTK(stuff...) printk (stuff)
|
||||
#else
|
||||
#define DPRINTK(stuff...)
|
||||
#endif
|
||||
|
||||
/* Make parport_wait_peripheral wake up.
|
||||
* It will be useful to call this from an interrupt handler. */
|
||||
static void parport_ieee1284_wakeup (struct parport *port)
|
||||
{
|
||||
up (&port->physport->ieee1284.irq);
|
||||
}
|
||||
|
||||
static struct parport *port_from_cookie[PARPORT_MAX];
|
||||
static void timeout_waiting_on_port (unsigned long cookie)
|
||||
{
|
||||
parport_ieee1284_wakeup (port_from_cookie[cookie % PARPORT_MAX]);
|
||||
}
|
||||
|
||||
/**
|
||||
* parport_wait_event - wait for an event on a parallel port
|
||||
* @port: port to wait on
|
||||
* @timeout: time to wait (in jiffies)
|
||||
*
|
||||
* This function waits for up to @timeout jiffies for an
|
||||
* interrupt to occur on a parallel port. If the port timeout is
|
||||
* set to zero, it returns immediately.
|
||||
*
|
||||
* If an interrupt occurs before the timeout period elapses, this
|
||||
* function returns zero immediately. If it times out, it returns
|
||||
* one. An error code less than zero indicates an error (most
|
||||
* likely a pending signal), and the calling code should finish
|
||||
* what it's doing as soon as it can.
|
||||
*/
|
||||
|
||||
int parport_wait_event (struct parport *port, signed long timeout)
|
||||
{
|
||||
int ret;
|
||||
struct timer_list timer;
|
||||
|
||||
if (!port->physport->cad->timeout)
|
||||
/* Zero timeout is special, and we can't down() the
|
||||
semaphore. */
|
||||
return 1;
|
||||
|
||||
init_timer_on_stack(&timer);
|
||||
timer.expires = jiffies + timeout;
|
||||
timer.function = timeout_waiting_on_port;
|
||||
port_from_cookie[port->number % PARPORT_MAX] = port;
|
||||
timer.data = port->number;
|
||||
|
||||
add_timer (&timer);
|
||||
ret = down_interruptible (&port->physport->ieee1284.irq);
|
||||
if (!del_timer_sync(&timer) && !ret)
|
||||
/* Timed out. */
|
||||
ret = 1;
|
||||
|
||||
destroy_timer_on_stack(&timer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* parport_poll_peripheral - poll status lines
|
||||
* @port: port to watch
|
||||
* @mask: status lines to watch
|
||||
* @result: desired values of chosen status lines
|
||||
* @usec: timeout
|
||||
*
|
||||
* This function busy-waits until the masked status lines have
|
||||
* the desired values, or until the timeout period elapses. The
|
||||
* @mask and @result parameters are bitmasks, with the bits
|
||||
* defined by the constants in parport.h: %PARPORT_STATUS_BUSY,
|
||||
* and so on.
|
||||
*
|
||||
* This function does not call schedule(); instead it busy-waits
|
||||
* using udelay(). It currently has a resolution of 5usec.
|
||||
*
|
||||
* If the status lines take on the desired values before the
|
||||
* timeout period elapses, parport_poll_peripheral() returns zero
|
||||
* immediately. A return value greater than zero indicates
|
||||
* a timeout. An error code (less than zero) indicates an error,
|
||||
* most likely a signal that arrived, and the caller should
|
||||
* finish what it is doing as soon as possible.
|
||||
*/
|
||||
|
||||
int parport_poll_peripheral(struct parport *port,
|
||||
unsigned char mask,
|
||||
unsigned char result,
|
||||
int usec)
|
||||
{
|
||||
/* Zero return code is success, >0 is timeout. */
|
||||
int count = usec / 5 + 2;
|
||||
int i;
|
||||
unsigned char status;
|
||||
for (i = 0; i < count; i++) {
|
||||
status = parport_read_status (port);
|
||||
if ((status & mask) == result)
|
||||
return 0;
|
||||
if (signal_pending (current))
|
||||
return -EINTR;
|
||||
if (need_resched())
|
||||
break;
|
||||
if (i >= 2)
|
||||
udelay (5);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* parport_wait_peripheral - wait for status lines to change in 35ms
|
||||
* @port: port to watch
|
||||
* @mask: status lines to watch
|
||||
* @result: desired values of chosen status lines
|
||||
*
|
||||
* This function waits until the masked status lines have the
|
||||
* desired values, or until 35ms have elapsed (see IEEE 1284-1994
|
||||
* page 24 to 25 for why this value in particular is hardcoded).
|
||||
* The @mask and @result parameters are bitmasks, with the bits
|
||||
* defined by the constants in parport.h: %PARPORT_STATUS_BUSY,
|
||||
* and so on.
|
||||
*
|
||||
* The port is polled quickly to start off with, in anticipation
|
||||
* of a fast response from the peripheral. This fast polling
|
||||
* time is configurable (using /proc), and defaults to 500usec.
|
||||
* If the timeout for this port (see parport_set_timeout()) is
|
||||
* zero, the fast polling time is 35ms, and this function does
|
||||
* not call schedule().
|
||||
*
|
||||
* If the timeout for this port is non-zero, after the fast
|
||||
* polling fails it uses parport_wait_event() to wait for up to
|
||||
* 10ms, waking up if an interrupt occurs.
|
||||
*/
|
||||
|
||||
int parport_wait_peripheral(struct parport *port,
|
||||
unsigned char mask,
|
||||
unsigned char result)
|
||||
{
|
||||
int ret;
|
||||
int usec;
|
||||
unsigned long deadline;
|
||||
unsigned char status;
|
||||
|
||||
usec = port->physport->spintime; /* usecs of fast polling */
|
||||
if (!port->physport->cad->timeout)
|
||||
/* A zero timeout is "special": busy wait for the
|
||||
entire 35ms. */
|
||||
usec = 35000;
|
||||
|
||||
/* Fast polling.
|
||||
*
|
||||
* This should be adjustable.
|
||||
* How about making a note (in the device structure) of how long
|
||||
* it takes, so we know for next time?
|
||||
*/
|
||||
ret = parport_poll_peripheral (port, mask, result, usec);
|
||||
if (ret != 1)
|
||||
return ret;
|
||||
|
||||
if (!port->physport->cad->timeout)
|
||||
/* We may be in an interrupt handler, so we can't poll
|
||||
* slowly anyway. */
|
||||
return 1;
|
||||
|
||||
/* 40ms of slow polling. */
|
||||
deadline = jiffies + msecs_to_jiffies(40);
|
||||
while (time_before (jiffies, deadline)) {
|
||||
if (signal_pending (current))
|
||||
return -EINTR;
|
||||
|
||||
/* Wait for 10ms (or until an interrupt occurs if
|
||||
* the handler is set) */
|
||||
if ((ret = parport_wait_event (port, msecs_to_jiffies(10))) < 0)
|
||||
return ret;
|
||||
|
||||
status = parport_read_status (port);
|
||||
if ((status & mask) == result)
|
||||
return 0;
|
||||
|
||||
if (!ret) {
|
||||
/* parport_wait_event didn't time out, but the
|
||||
* peripheral wasn't actually ready either.
|
||||
* Wait for another 10ms. */
|
||||
schedule_timeout_interruptible(msecs_to_jiffies(10));
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PARPORT_1284
|
||||
/* Terminate a negotiated mode. */
|
||||
static void parport_ieee1284_terminate (struct parport *port)
|
||||
{
|
||||
int r;
|
||||
port = port->physport;
|
||||
|
||||
/* EPP terminates differently. */
|
||||
switch (port->ieee1284.mode) {
|
||||
case IEEE1284_MODE_EPP:
|
||||
case IEEE1284_MODE_EPPSL:
|
||||
case IEEE1284_MODE_EPPSWE:
|
||||
/* Terminate from EPP mode. */
|
||||
|
||||
/* Event 68: Set nInit low */
|
||||
parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
|
||||
udelay (50);
|
||||
|
||||
/* Event 69: Set nInit high, nSelectIn low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_SELECT
|
||||
| PARPORT_CONTROL_INIT,
|
||||
PARPORT_CONTROL_SELECT
|
||||
| PARPORT_CONTROL_INIT);
|
||||
break;
|
||||
|
||||
case IEEE1284_MODE_ECP:
|
||||
case IEEE1284_MODE_ECPRLE:
|
||||
case IEEE1284_MODE_ECPSWE:
|
||||
/* In ECP we can only terminate from fwd idle phase. */
|
||||
if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE) {
|
||||
/* Event 47: Set nInit high */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_INIT
|
||||
| PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_INIT
|
||||
| PARPORT_CONTROL_AUTOFD);
|
||||
|
||||
/* Event 49: PError goes high */
|
||||
r = parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_PAPEROUT,
|
||||
PARPORT_STATUS_PAPEROUT);
|
||||
if (r)
|
||||
DPRINTK (KERN_INFO "%s: Timeout at event 49\n",
|
||||
port->name);
|
||||
|
||||
parport_data_forward (port);
|
||||
DPRINTK (KERN_DEBUG "%s: ECP direction: forward\n",
|
||||
port->name);
|
||||
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
||||
}
|
||||
|
||||
/* fall-though.. */
|
||||
|
||||
default:
|
||||
/* Terminate from all other modes. */
|
||||
|
||||
/* Event 22: Set nSelectIn low, nAutoFd high */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_SELECT
|
||||
| PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_SELECT);
|
||||
|
||||
/* Event 24: nAck goes low */
|
||||
r = parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0);
|
||||
if (r)
|
||||
DPRINTK (KERN_INFO "%s: Timeout at event 24\n",
|
||||
port->name);
|
||||
|
||||
/* Event 25: Set nAutoFd low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
|
||||
/* Event 27: nAck goes high */
|
||||
r = parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_ACK,
|
||||
PARPORT_STATUS_ACK);
|
||||
if (r)
|
||||
DPRINTK (KERN_INFO "%s: Timeout at event 27\n",
|
||||
port->name);
|
||||
|
||||
/* Event 29: Set nAutoFd high */
|
||||
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
||||
}
|
||||
|
||||
port->ieee1284.mode = IEEE1284_MODE_COMPAT;
|
||||
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
||||
|
||||
DPRINTK (KERN_DEBUG "%s: In compatibility (forward idle) mode\n",
|
||||
port->name);
|
||||
}
|
||||
#endif /* IEEE1284 support */
|
||||
|
||||
/**
|
||||
* parport_negotiate - negotiate an IEEE 1284 mode
|
||||
* @port: port to use
|
||||
* @mode: mode to negotiate to
|
||||
*
|
||||
* Use this to negotiate to a particular IEEE 1284 transfer mode.
|
||||
* The @mode parameter should be one of the constants in
|
||||
* parport.h starting %IEEE1284_MODE_xxx.
|
||||
*
|
||||
* The return value is 0 if the peripheral has accepted the
|
||||
* negotiation to the mode specified, -1 if the peripheral is not
|
||||
* IEEE 1284 compliant (or not present), or 1 if the peripheral
|
||||
* has rejected the negotiation.
|
||||
*/
|
||||
|
||||
int parport_negotiate (struct parport *port, int mode)
|
||||
{
|
||||
#ifndef CONFIG_PARPORT_1284
|
||||
if (mode == IEEE1284_MODE_COMPAT)
|
||||
return 0;
|
||||
printk (KERN_ERR "parport: IEEE1284 not supported in this kernel\n");
|
||||
return -1;
|
||||
#else
|
||||
int m = mode & ~IEEE1284_ADDR;
|
||||
int r;
|
||||
unsigned char xflag;
|
||||
|
||||
port = port->physport;
|
||||
|
||||
/* Is there anything to do? */
|
||||
if (port->ieee1284.mode == mode)
|
||||
return 0;
|
||||
|
||||
/* Is the difference just an address-or-not bit? */
|
||||
if ((port->ieee1284.mode & ~IEEE1284_ADDR) == (mode & ~IEEE1284_ADDR)){
|
||||
port->ieee1284.mode = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Go to compatibility forward idle mode */
|
||||
if (port->ieee1284.mode != IEEE1284_MODE_COMPAT)
|
||||
parport_ieee1284_terminate (port);
|
||||
|
||||
if (mode == IEEE1284_MODE_COMPAT)
|
||||
/* Compatibility mode: no negotiation. */
|
||||
return 0;
|
||||
|
||||
switch (mode) {
|
||||
case IEEE1284_MODE_ECPSWE:
|
||||
m = IEEE1284_MODE_ECP;
|
||||
break;
|
||||
case IEEE1284_MODE_EPPSL:
|
||||
case IEEE1284_MODE_EPPSWE:
|
||||
m = IEEE1284_MODE_EPP;
|
||||
break;
|
||||
case IEEE1284_MODE_BECP:
|
||||
return -ENOSYS; /* FIXME (implement BECP) */
|
||||
}
|
||||
|
||||
if (mode & IEEE1284_EXT_LINK)
|
||||
m = 1<<7; /* request extensibility link */
|
||||
|
||||
port->ieee1284.phase = IEEE1284_PH_NEGOTIATION;
|
||||
|
||||
/* Start off with nStrobe and nAutoFd high, and nSelectIn low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE
|
||||
| PARPORT_CONTROL_AUTOFD
|
||||
| PARPORT_CONTROL_SELECT,
|
||||
PARPORT_CONTROL_SELECT);
|
||||
udelay(1);
|
||||
|
||||
/* Event 0: Set data */
|
||||
parport_data_forward (port);
|
||||
parport_write_data (port, m);
|
||||
udelay (400); /* Shouldn't need to wait this long. */
|
||||
|
||||
/* Event 1: Set nSelectIn high, nAutoFd low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_SELECT
|
||||
| PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
|
||||
/* Event 2: PError, Select, nFault go high, nAck goes low */
|
||||
if (parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_ERROR
|
||||
| PARPORT_STATUS_SELECT
|
||||
| PARPORT_STATUS_PAPEROUT
|
||||
| PARPORT_STATUS_ACK,
|
||||
PARPORT_STATUS_ERROR
|
||||
| PARPORT_STATUS_SELECT
|
||||
| PARPORT_STATUS_PAPEROUT)) {
|
||||
/* Timeout */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_SELECT
|
||||
| PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_SELECT);
|
||||
DPRINTK (KERN_DEBUG
|
||||
"%s: Peripheral not IEEE1284 compliant (0x%02X)\n",
|
||||
port->name, parport_read_status (port));
|
||||
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
||||
return -1; /* Not IEEE1284 compliant */
|
||||
}
|
||||
|
||||
/* Event 3: Set nStrobe low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE,
|
||||
PARPORT_CONTROL_STROBE);
|
||||
|
||||
/* Event 4: Set nStrobe and nAutoFd high */
|
||||
udelay (5);
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE
|
||||
| PARPORT_CONTROL_AUTOFD,
|
||||
0);
|
||||
|
||||
/* Event 6: nAck goes high */
|
||||
if (parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_ACK,
|
||||
PARPORT_STATUS_ACK)) {
|
||||
/* This shouldn't really happen with a compliant device. */
|
||||
DPRINTK (KERN_DEBUG
|
||||
"%s: Mode 0x%02x not supported? (0x%02x)\n",
|
||||
port->name, mode, port->ops->read_status (port));
|
||||
parport_ieee1284_terminate (port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
xflag = parport_read_status (port) & PARPORT_STATUS_SELECT;
|
||||
|
||||
/* xflag should be high for all modes other than nibble (0). */
|
||||
if (mode && !xflag) {
|
||||
/* Mode not supported. */
|
||||
DPRINTK (KERN_DEBUG "%s: Mode 0x%02x rejected by peripheral\n",
|
||||
port->name, mode);
|
||||
parport_ieee1284_terminate (port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* More to do if we've requested extensibility link. */
|
||||
if (mode & IEEE1284_EXT_LINK) {
|
||||
m = mode & 0x7f;
|
||||
udelay (1);
|
||||
parport_write_data (port, m);
|
||||
udelay (1);
|
||||
|
||||
/* Event 51: Set nStrobe low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE,
|
||||
PARPORT_CONTROL_STROBE);
|
||||
|
||||
/* Event 52: nAck goes low */
|
||||
if (parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0)) {
|
||||
/* This peripheral is _very_ slow. */
|
||||
DPRINTK (KERN_DEBUG
|
||||
"%s: Event 52 didn't happen\n",
|
||||
port->name);
|
||||
parport_ieee1284_terminate (port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Event 53: Set nStrobe high */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE,
|
||||
0);
|
||||
|
||||
/* Event 55: nAck goes high */
|
||||
if (parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_ACK,
|
||||
PARPORT_STATUS_ACK)) {
|
||||
/* This shouldn't really happen with a compliant
|
||||
* device. */
|
||||
DPRINTK (KERN_DEBUG
|
||||
"%s: Mode 0x%02x not supported? (0x%02x)\n",
|
||||
port->name, mode,
|
||||
port->ops->read_status (port));
|
||||
parport_ieee1284_terminate (port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Event 54: Peripheral sets XFlag to reflect support */
|
||||
xflag = parport_read_status (port) & PARPORT_STATUS_SELECT;
|
||||
|
||||
/* xflag should be high. */
|
||||
if (!xflag) {
|
||||
/* Extended mode not supported. */
|
||||
DPRINTK (KERN_DEBUG "%s: Extended mode 0x%02x not "
|
||||
"supported\n", port->name, mode);
|
||||
parport_ieee1284_terminate (port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Any further setup is left to the caller. */
|
||||
}
|
||||
|
||||
/* Mode is supported */
|
||||
DPRINTK (KERN_DEBUG "%s: In mode 0x%02x\n", port->name, mode);
|
||||
port->ieee1284.mode = mode;
|
||||
|
||||
/* But ECP is special */
|
||||
if (!(mode & IEEE1284_EXT_LINK) && (m & IEEE1284_MODE_ECP)) {
|
||||
port->ieee1284.phase = IEEE1284_PH_ECP_SETUP;
|
||||
|
||||
/* Event 30: Set nAutoFd low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
|
||||
/* Event 31: PError goes high. */
|
||||
r = parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_PAPEROUT,
|
||||
PARPORT_STATUS_PAPEROUT);
|
||||
if (r) {
|
||||
DPRINTK (KERN_INFO "%s: Timeout at event 31\n",
|
||||
port->name);
|
||||
}
|
||||
|
||||
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
||||
DPRINTK (KERN_DEBUG "%s: ECP direction: forward\n",
|
||||
port->name);
|
||||
} else switch (mode) {
|
||||
case IEEE1284_MODE_NIBBLE:
|
||||
case IEEE1284_MODE_BYTE:
|
||||
port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
|
||||
break;
|
||||
default:
|
||||
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
#endif /* IEEE1284 support */
|
||||
}
|
||||
|
||||
/* Acknowledge that the peripheral has data available.
|
||||
* Events 18-20, in order to get from Reverse Idle phase
|
||||
* to Host Busy Data Available.
|
||||
* This will most likely be called from an interrupt.
|
||||
* Returns zero if data was available.
|
||||
*/
|
||||
#ifdef CONFIG_PARPORT_1284
|
||||
static int parport_ieee1284_ack_data_avail (struct parport *port)
|
||||
{
|
||||
if (parport_read_status (port) & PARPORT_STATUS_ERROR)
|
||||
/* Event 18 didn't happen. */
|
||||
return -1;
|
||||
|
||||
/* Event 20: nAutoFd goes high. */
|
||||
port->ops->frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
||||
port->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
|
||||
return 0;
|
||||
}
|
||||
#endif /* IEEE1284 support */
|
||||
|
||||
/* Handle an interrupt. */
|
||||
void parport_ieee1284_interrupt (void *handle)
|
||||
{
|
||||
struct parport *port = handle;
|
||||
parport_ieee1284_wakeup (port);
|
||||
|
||||
#ifdef CONFIG_PARPORT_1284
|
||||
if (port->ieee1284.phase == IEEE1284_PH_REV_IDLE) {
|
||||
/* An interrupt in this phase means that data
|
||||
* is now available. */
|
||||
DPRINTK (KERN_DEBUG "%s: Data available\n", port->name);
|
||||
parport_ieee1284_ack_data_avail (port);
|
||||
}
|
||||
#endif /* IEEE1284 support */
|
||||
}
|
||||
|
||||
/**
|
||||
* parport_write - write a block of data to a parallel port
|
||||
* @port: port to write to
|
||||
* @buffer: data buffer (in kernel space)
|
||||
* @len: number of bytes of data to transfer
|
||||
*
|
||||
* This will write up to @len bytes of @buffer to the port
|
||||
* specified, using the IEEE 1284 transfer mode most recently
|
||||
* negotiated to (using parport_negotiate()), as long as that
|
||||
* mode supports forward transfers (host to peripheral).
|
||||
*
|
||||
* It is the caller's responsibility to ensure that the first
|
||||
* @len bytes of @buffer are valid.
|
||||
*
|
||||
* This function returns the number of bytes transferred (if zero
|
||||
* or positive), or else an error code.
|
||||
*/
|
||||
|
||||
ssize_t parport_write (struct parport *port, const void *buffer, size_t len)
|
||||
{
|
||||
#ifndef CONFIG_PARPORT_1284
|
||||
return port->ops->compat_write_data (port, buffer, len, 0);
|
||||
#else
|
||||
ssize_t retval;
|
||||
int mode = port->ieee1284.mode;
|
||||
int addr = mode & IEEE1284_ADDR;
|
||||
size_t (*fn) (struct parport *, const void *, size_t, int);
|
||||
|
||||
/* Ignore the device-ID-request bit and the address bit. */
|
||||
mode &= ~(IEEE1284_DEVICEID | IEEE1284_ADDR);
|
||||
|
||||
/* Use the mode we're in. */
|
||||
switch (mode) {
|
||||
case IEEE1284_MODE_NIBBLE:
|
||||
case IEEE1284_MODE_BYTE:
|
||||
parport_negotiate (port, IEEE1284_MODE_COMPAT);
|
||||
case IEEE1284_MODE_COMPAT:
|
||||
DPRINTK (KERN_DEBUG "%s: Using compatibility mode\n",
|
||||
port->name);
|
||||
fn = port->ops->compat_write_data;
|
||||
break;
|
||||
|
||||
case IEEE1284_MODE_EPP:
|
||||
DPRINTK (KERN_DEBUG "%s: Using EPP mode\n", port->name);
|
||||
if (addr) {
|
||||
fn = port->ops->epp_write_addr;
|
||||
} else {
|
||||
fn = port->ops->epp_write_data;
|
||||
}
|
||||
break;
|
||||
case IEEE1284_MODE_EPPSWE:
|
||||
DPRINTK (KERN_DEBUG "%s: Using software-emulated EPP mode\n",
|
||||
port->name);
|
||||
if (addr) {
|
||||
fn = parport_ieee1284_epp_write_addr;
|
||||
} else {
|
||||
fn = parport_ieee1284_epp_write_data;
|
||||
}
|
||||
break;
|
||||
case IEEE1284_MODE_ECP:
|
||||
case IEEE1284_MODE_ECPRLE:
|
||||
DPRINTK (KERN_DEBUG "%s: Using ECP mode\n", port->name);
|
||||
if (addr) {
|
||||
fn = port->ops->ecp_write_addr;
|
||||
} else {
|
||||
fn = port->ops->ecp_write_data;
|
||||
}
|
||||
break;
|
||||
|
||||
case IEEE1284_MODE_ECPSWE:
|
||||
DPRINTK (KERN_DEBUG "%s: Using software-emulated ECP mode\n",
|
||||
port->name);
|
||||
/* The caller has specified that it must be emulated,
|
||||
* even if we have ECP hardware! */
|
||||
if (addr) {
|
||||
fn = parport_ieee1284_ecp_write_addr;
|
||||
} else {
|
||||
fn = parport_ieee1284_ecp_write_data;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
DPRINTK (KERN_DEBUG "%s: Unknown mode 0x%02x\n", port->name,
|
||||
port->ieee1284.mode);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
retval = (*fn) (port, buffer, len, 0);
|
||||
DPRINTK (KERN_DEBUG "%s: wrote %d/%d bytes\n", port->name, retval, len);
|
||||
return retval;
|
||||
#endif /* IEEE1284 support */
|
||||
}
|
||||
|
||||
/**
|
||||
* parport_read - read a block of data from a parallel port
|
||||
* @port: port to read from
|
||||
* @buffer: data buffer (in kernel space)
|
||||
* @len: number of bytes of data to transfer
|
||||
*
|
||||
* This will read up to @len bytes of @buffer to the port
|
||||
* specified, using the IEEE 1284 transfer mode most recently
|
||||
* negotiated to (using parport_negotiate()), as long as that
|
||||
* mode supports reverse transfers (peripheral to host).
|
||||
*
|
||||
* It is the caller's responsibility to ensure that the first
|
||||
* @len bytes of @buffer are available to write to.
|
||||
*
|
||||
* This function returns the number of bytes transferred (if zero
|
||||
* or positive), or else an error code.
|
||||
*/
|
||||
|
||||
ssize_t parport_read (struct parport *port, void *buffer, size_t len)
|
||||
{
|
||||
#ifndef CONFIG_PARPORT_1284
|
||||
printk (KERN_ERR "parport: IEEE1284 not supported in this kernel\n");
|
||||
return -ENODEV;
|
||||
#else
|
||||
int mode = port->physport->ieee1284.mode;
|
||||
int addr = mode & IEEE1284_ADDR;
|
||||
size_t (*fn) (struct parport *, void *, size_t, int);
|
||||
|
||||
/* Ignore the device-ID-request bit and the address bit. */
|
||||
mode &= ~(IEEE1284_DEVICEID | IEEE1284_ADDR);
|
||||
|
||||
/* Use the mode we're in. */
|
||||
switch (mode) {
|
||||
case IEEE1284_MODE_COMPAT:
|
||||
/* if we can tri-state use BYTE mode instead of NIBBLE mode,
|
||||
* if that fails, revert to NIBBLE mode -- ought to store somewhere
|
||||
* the device's ability to do BYTE mode reverse transfers, so we don't
|
||||
* end up needlessly calling negotiate(BYTE) repeately.. (fb)
|
||||
*/
|
||||
if ((port->physport->modes & PARPORT_MODE_TRISTATE) &&
|
||||
!parport_negotiate (port, IEEE1284_MODE_BYTE)) {
|
||||
/* got into BYTE mode OK */
|
||||
DPRINTK (KERN_DEBUG "%s: Using byte mode\n", port->name);
|
||||
fn = port->ops->byte_read_data;
|
||||
break;
|
||||
}
|
||||
if (parport_negotiate (port, IEEE1284_MODE_NIBBLE)) {
|
||||
return -EIO;
|
||||
}
|
||||
/* fall through to NIBBLE */
|
||||
case IEEE1284_MODE_NIBBLE:
|
||||
DPRINTK (KERN_DEBUG "%s: Using nibble mode\n", port->name);
|
||||
fn = port->ops->nibble_read_data;
|
||||
break;
|
||||
|
||||
case IEEE1284_MODE_BYTE:
|
||||
DPRINTK (KERN_DEBUG "%s: Using byte mode\n", port->name);
|
||||
fn = port->ops->byte_read_data;
|
||||
break;
|
||||
|
||||
case IEEE1284_MODE_EPP:
|
||||
DPRINTK (KERN_DEBUG "%s: Using EPP mode\n", port->name);
|
||||
if (addr) {
|
||||
fn = port->ops->epp_read_addr;
|
||||
} else {
|
||||
fn = port->ops->epp_read_data;
|
||||
}
|
||||
break;
|
||||
case IEEE1284_MODE_EPPSWE:
|
||||
DPRINTK (KERN_DEBUG "%s: Using software-emulated EPP mode\n",
|
||||
port->name);
|
||||
if (addr) {
|
||||
fn = parport_ieee1284_epp_read_addr;
|
||||
} else {
|
||||
fn = parport_ieee1284_epp_read_data;
|
||||
}
|
||||
break;
|
||||
case IEEE1284_MODE_ECP:
|
||||
case IEEE1284_MODE_ECPRLE:
|
||||
DPRINTK (KERN_DEBUG "%s: Using ECP mode\n", port->name);
|
||||
fn = port->ops->ecp_read_data;
|
||||
break;
|
||||
|
||||
case IEEE1284_MODE_ECPSWE:
|
||||
DPRINTK (KERN_DEBUG "%s: Using software-emulated ECP mode\n",
|
||||
port->name);
|
||||
fn = parport_ieee1284_ecp_read_data;
|
||||
break;
|
||||
|
||||
default:
|
||||
DPRINTK (KERN_DEBUG "%s: Unknown mode 0x%02x\n", port->name,
|
||||
port->physport->ieee1284.mode);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
return (*fn) (port, buffer, len, 0);
|
||||
#endif /* IEEE1284 support */
|
||||
}
|
||||
|
||||
/**
|
||||
* parport_set_timeout - set the inactivity timeout for a device
|
||||
* @dev: device on a port
|
||||
* @inactivity: inactivity timeout (in jiffies)
|
||||
*
|
||||
* This sets the inactivity timeout for a particular device on a
|
||||
* port. This affects functions like parport_wait_peripheral().
|
||||
* The special value 0 means not to call schedule() while dealing
|
||||
* with this device.
|
||||
*
|
||||
* The return value is the previous inactivity timeout.
|
||||
*
|
||||
* Any callers of parport_wait_event() for this device are woken
|
||||
* up.
|
||||
*/
|
||||
|
||||
long parport_set_timeout (struct pardevice *dev, long inactivity)
|
||||
{
|
||||
long int old = dev->timeout;
|
||||
|
||||
dev->timeout = inactivity;
|
||||
|
||||
if (dev->port->physport->cad == dev)
|
||||
parport_ieee1284_wakeup (dev->port);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
/* Exported symbols for modules. */
|
||||
|
||||
EXPORT_SYMBOL(parport_negotiate);
|
||||
EXPORT_SYMBOL(parport_write);
|
||||
EXPORT_SYMBOL(parport_read);
|
||||
EXPORT_SYMBOL(parport_wait_peripheral);
|
||||
EXPORT_SYMBOL(parport_wait_event);
|
||||
EXPORT_SYMBOL(parport_set_timeout);
|
||||
EXPORT_SYMBOL(parport_ieee1284_interrupt);
|
914
drivers/parport/ieee1284_ops.c
Normal file
914
drivers/parport/ieee1284_ops.c
Normal file
|
@ -0,0 +1,914 @@
|
|||
/* IEEE-1284 operations for parport.
|
||||
*
|
||||
* This file is for generic IEEE 1284 operations. The idea is that
|
||||
* they are used by the low-level drivers. If they have a special way
|
||||
* of doing something, they can provide their own routines (and put
|
||||
* the function pointers in port->ops); if not, they can just use these
|
||||
* as a fallback.
|
||||
*
|
||||
* Note: Make no assumptions about hardware or architecture in this file!
|
||||
*
|
||||
* Author: Tim Waugh <tim@cyberelk.demon.co.uk>
|
||||
* Fixed AUTOFD polarity in ecp_forward_to_reverse(). Fred Barnes, 1999
|
||||
* Software emulated EPP fixes, Fred Barnes, 04/2001.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#undef DEBUG /* undef me for production */
|
||||
|
||||
#ifdef CONFIG_LP_CONSOLE
|
||||
#undef DEBUG /* Don't want a garbled console */
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DPRINTK(stuff...) printk (stuff)
|
||||
#else
|
||||
#define DPRINTK(stuff...)
|
||||
#endif
|
||||
|
||||
/*** *
|
||||
* One-way data transfer functions. *
|
||||
* ***/
|
||||
|
||||
/* Compatibility mode. */
|
||||
size_t parport_ieee1284_write_compat (struct parport *port,
|
||||
const void *buffer, size_t len,
|
||||
int flags)
|
||||
{
|
||||
int no_irq = 1;
|
||||
ssize_t count = 0;
|
||||
const unsigned char *addr = buffer;
|
||||
unsigned char byte;
|
||||
struct pardevice *dev = port->physport->cad;
|
||||
unsigned char ctl = (PARPORT_CONTROL_SELECT
|
||||
| PARPORT_CONTROL_INIT);
|
||||
|
||||
if (port->irq != PARPORT_IRQ_NONE) {
|
||||
parport_enable_irq (port);
|
||||
no_irq = 0;
|
||||
}
|
||||
|
||||
port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
|
||||
parport_write_control (port, ctl);
|
||||
parport_data_forward (port);
|
||||
while (count < len) {
|
||||
unsigned long expire = jiffies + dev->timeout;
|
||||
long wait = msecs_to_jiffies(10);
|
||||
unsigned char mask = (PARPORT_STATUS_ERROR
|
||||
| PARPORT_STATUS_BUSY);
|
||||
unsigned char val = (PARPORT_STATUS_ERROR
|
||||
| PARPORT_STATUS_BUSY);
|
||||
|
||||
/* Wait until the peripheral's ready */
|
||||
do {
|
||||
/* Is the peripheral ready yet? */
|
||||
if (!parport_wait_peripheral (port, mask, val))
|
||||
/* Skip the loop */
|
||||
goto ready;
|
||||
|
||||
/* Is the peripheral upset? */
|
||||
if ((parport_read_status (port) &
|
||||
(PARPORT_STATUS_PAPEROUT |
|
||||
PARPORT_STATUS_SELECT |
|
||||
PARPORT_STATUS_ERROR))
|
||||
!= (PARPORT_STATUS_SELECT |
|
||||
PARPORT_STATUS_ERROR))
|
||||
/* If nFault is asserted (i.e. no
|
||||
* error) and PAPEROUT and SELECT are
|
||||
* just red herrings, give the driver
|
||||
* a chance to check it's happy with
|
||||
* that before continuing. */
|
||||
goto stop;
|
||||
|
||||
/* Have we run out of time? */
|
||||
if (!time_before (jiffies, expire))
|
||||
break;
|
||||
|
||||
/* Yield the port for a while. If this is the
|
||||
first time around the loop, don't let go of
|
||||
the port. This way, we find out if we have
|
||||
our interrupt handler called. */
|
||||
if (count && no_irq) {
|
||||
parport_release (dev);
|
||||
schedule_timeout_interruptible(wait);
|
||||
parport_claim_or_block (dev);
|
||||
}
|
||||
else
|
||||
/* We must have the device claimed here */
|
||||
parport_wait_event (port, wait);
|
||||
|
||||
/* Is there a signal pending? */
|
||||
if (signal_pending (current))
|
||||
break;
|
||||
|
||||
/* Wait longer next time. */
|
||||
wait *= 2;
|
||||
} while (time_before (jiffies, expire));
|
||||
|
||||
if (signal_pending (current))
|
||||
break;
|
||||
|
||||
DPRINTK (KERN_DEBUG "%s: Timed out\n", port->name);
|
||||
break;
|
||||
|
||||
ready:
|
||||
/* Write the character to the data lines. */
|
||||
byte = *addr++;
|
||||
parport_write_data (port, byte);
|
||||
udelay (1);
|
||||
|
||||
/* Pulse strobe. */
|
||||
parport_write_control (port, ctl | PARPORT_CONTROL_STROBE);
|
||||
udelay (1); /* strobe */
|
||||
|
||||
parport_write_control (port, ctl);
|
||||
udelay (1); /* hold */
|
||||
|
||||
/* Assume the peripheral received it. */
|
||||
count++;
|
||||
|
||||
/* Let another process run if it needs to. */
|
||||
if (time_before (jiffies, expire))
|
||||
if (!parport_yield_blocking (dev)
|
||||
&& need_resched())
|
||||
schedule ();
|
||||
}
|
||||
stop:
|
||||
port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Nibble mode. */
|
||||
size_t parport_ieee1284_read_nibble (struct parport *port,
|
||||
void *buffer, size_t len,
|
||||
int flags)
|
||||
{
|
||||
#ifndef CONFIG_PARPORT_1284
|
||||
return 0;
|
||||
#else
|
||||
unsigned char *buf = buffer;
|
||||
int i;
|
||||
unsigned char byte = 0;
|
||||
|
||||
len *= 2; /* in nibbles */
|
||||
for (i=0; i < len; i++) {
|
||||
unsigned char nibble;
|
||||
|
||||
/* Does the error line indicate end of data? */
|
||||
if (((i & 1) == 0) &&
|
||||
(parport_read_status(port) & PARPORT_STATUS_ERROR)) {
|
||||
goto end_of_data;
|
||||
}
|
||||
|
||||
/* Event 7: Set nAutoFd low. */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
|
||||
/* Event 9: nAck goes low. */
|
||||
port->ieee1284.phase = IEEE1284_PH_REV_DATA;
|
||||
if (parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_ACK, 0)) {
|
||||
/* Timeout -- no more data? */
|
||||
DPRINTK (KERN_DEBUG
|
||||
"%s: Nibble timeout at event 9 (%d bytes)\n",
|
||||
port->name, i/2);
|
||||
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Read a nibble. */
|
||||
nibble = parport_read_status (port) >> 3;
|
||||
nibble &= ~8;
|
||||
if ((nibble & 0x10) == 0)
|
||||
nibble |= 8;
|
||||
nibble &= 0xf;
|
||||
|
||||
/* Event 10: Set nAutoFd high. */
|
||||
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
||||
|
||||
/* Event 11: nAck goes high. */
|
||||
if (parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_ACK,
|
||||
PARPORT_STATUS_ACK)) {
|
||||
/* Timeout -- no more data? */
|
||||
DPRINTK (KERN_DEBUG
|
||||
"%s: Nibble timeout at event 11\n",
|
||||
port->name);
|
||||
break;
|
||||
}
|
||||
|
||||
if (i & 1) {
|
||||
/* Second nibble */
|
||||
byte |= nibble << 4;
|
||||
*buf++ = byte;
|
||||
} else
|
||||
byte = nibble;
|
||||
}
|
||||
|
||||
if (i == len) {
|
||||
/* Read the last nibble without checking data avail. */
|
||||
if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
|
||||
end_of_data:
|
||||
DPRINTK (KERN_DEBUG
|
||||
"%s: No more nibble data (%d bytes)\n",
|
||||
port->name, i/2);
|
||||
|
||||
/* Go to reverse idle phase. */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
|
||||
}
|
||||
else
|
||||
port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
|
||||
}
|
||||
|
||||
return i/2;
|
||||
#endif /* IEEE1284 support */
|
||||
}
|
||||
|
||||
/* Byte mode. */
|
||||
size_t parport_ieee1284_read_byte (struct parport *port,
|
||||
void *buffer, size_t len,
|
||||
int flags)
|
||||
{
|
||||
#ifndef CONFIG_PARPORT_1284
|
||||
return 0;
|
||||
#else
|
||||
unsigned char *buf = buffer;
|
||||
ssize_t count = 0;
|
||||
|
||||
for (count = 0; count < len; count++) {
|
||||
unsigned char byte;
|
||||
|
||||
/* Data available? */
|
||||
if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
|
||||
goto end_of_data;
|
||||
}
|
||||
|
||||
/* Event 14: Place data bus in high impedance state. */
|
||||
parport_data_reverse (port);
|
||||
|
||||
/* Event 7: Set nAutoFd low. */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
|
||||
/* Event 9: nAck goes low. */
|
||||
port->physport->ieee1284.phase = IEEE1284_PH_REV_DATA;
|
||||
if (parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_ACK,
|
||||
0)) {
|
||||
/* Timeout -- no more data? */
|
||||
parport_frob_control (port, PARPORT_CONTROL_AUTOFD,
|
||||
0);
|
||||
DPRINTK (KERN_DEBUG "%s: Byte timeout at event 9\n",
|
||||
port->name);
|
||||
break;
|
||||
}
|
||||
|
||||
byte = parport_read_data (port);
|
||||
*buf++ = byte;
|
||||
|
||||
/* Event 10: Set nAutoFd high */
|
||||
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
||||
|
||||
/* Event 11: nAck goes high. */
|
||||
if (parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_ACK,
|
||||
PARPORT_STATUS_ACK)) {
|
||||
/* Timeout -- no more data? */
|
||||
DPRINTK (KERN_DEBUG "%s: Byte timeout at event 11\n",
|
||||
port->name);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Event 16: Set nStrobe low. */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE,
|
||||
PARPORT_CONTROL_STROBE);
|
||||
udelay (5);
|
||||
|
||||
/* Event 17: Set nStrobe high. */
|
||||
parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
|
||||
}
|
||||
|
||||
if (count == len) {
|
||||
/* Read the last byte without checking data avail. */
|
||||
if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
|
||||
end_of_data:
|
||||
DPRINTK (KERN_DEBUG
|
||||
"%s: No more byte data (%Zd bytes)\n",
|
||||
port->name, count);
|
||||
|
||||
/* Go to reverse idle phase. */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
|
||||
}
|
||||
else
|
||||
port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
|
||||
}
|
||||
|
||||
return count;
|
||||
#endif /* IEEE1284 support */
|
||||
}
|
||||
|
||||
/*** *
|
||||
* ECP Functions. *
|
||||
* ***/
|
||||
|
||||
#ifdef CONFIG_PARPORT_1284
|
||||
|
||||
static inline
|
||||
int ecp_forward_to_reverse (struct parport *port)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/* Event 38: Set nAutoFd low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
parport_data_reverse (port);
|
||||
udelay (5);
|
||||
|
||||
/* Event 39: Set nInit low to initiate bus reversal */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_INIT,
|
||||
0);
|
||||
|
||||
/* Event 40: PError goes low */
|
||||
retval = parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_PAPEROUT, 0);
|
||||
|
||||
if (!retval) {
|
||||
DPRINTK (KERN_DEBUG "%s: ECP direction: reverse\n",
|
||||
port->name);
|
||||
port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
|
||||
} else {
|
||||
DPRINTK (KERN_DEBUG "%s: ECP direction: failed to reverse\n",
|
||||
port->name);
|
||||
port->ieee1284.phase = IEEE1284_PH_ECP_DIR_UNKNOWN;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static inline
|
||||
int ecp_reverse_to_forward (struct parport *port)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/* Event 47: Set nInit high */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_INIT
|
||||
| PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_INIT
|
||||
| PARPORT_CONTROL_AUTOFD);
|
||||
|
||||
/* Event 49: PError goes high */
|
||||
retval = parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_PAPEROUT,
|
||||
PARPORT_STATUS_PAPEROUT);
|
||||
|
||||
if (!retval) {
|
||||
parport_data_forward (port);
|
||||
DPRINTK (KERN_DEBUG "%s: ECP direction: forward\n",
|
||||
port->name);
|
||||
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
||||
} else {
|
||||
DPRINTK (KERN_DEBUG
|
||||
"%s: ECP direction: failed to switch forward\n",
|
||||
port->name);
|
||||
port->ieee1284.phase = IEEE1284_PH_ECP_DIR_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif /* IEEE1284 support */
|
||||
|
||||
/* ECP mode, forward channel, data. */
|
||||
size_t parport_ieee1284_ecp_write_data (struct parport *port,
|
||||
const void *buffer, size_t len,
|
||||
int flags)
|
||||
{
|
||||
#ifndef CONFIG_PARPORT_1284
|
||||
return 0;
|
||||
#else
|
||||
const unsigned char *buf = buffer;
|
||||
size_t written;
|
||||
int retry;
|
||||
|
||||
port = port->physport;
|
||||
|
||||
if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE)
|
||||
if (ecp_reverse_to_forward (port))
|
||||
return 0;
|
||||
|
||||
port->ieee1284.phase = IEEE1284_PH_FWD_DATA;
|
||||
|
||||
/* HostAck high (data, not command) */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_AUTOFD
|
||||
| PARPORT_CONTROL_STROBE
|
||||
| PARPORT_CONTROL_INIT,
|
||||
PARPORT_CONTROL_INIT);
|
||||
for (written = 0; written < len; written++, buf++) {
|
||||
unsigned long expire = jiffies + port->cad->timeout;
|
||||
unsigned char byte;
|
||||
|
||||
byte = *buf;
|
||||
try_again:
|
||||
parport_write_data (port, byte);
|
||||
parport_frob_control (port, PARPORT_CONTROL_STROBE,
|
||||
PARPORT_CONTROL_STROBE);
|
||||
udelay (5);
|
||||
for (retry = 0; retry < 100; retry++) {
|
||||
if (!parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_BUSY, 0))
|
||||
goto success;
|
||||
|
||||
if (signal_pending (current)) {
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE,
|
||||
0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Time for Host Transfer Recovery (page 41 of IEEE1284) */
|
||||
DPRINTK (KERN_DEBUG "%s: ECP transfer stalled!\n", port->name);
|
||||
|
||||
parport_frob_control (port, PARPORT_CONTROL_INIT,
|
||||
PARPORT_CONTROL_INIT);
|
||||
udelay (50);
|
||||
if (parport_read_status (port) & PARPORT_STATUS_PAPEROUT) {
|
||||
/* It's buggered. */
|
||||
parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
|
||||
udelay (50);
|
||||
if (!(parport_read_status (port) & PARPORT_STATUS_PAPEROUT))
|
||||
break;
|
||||
|
||||
DPRINTK (KERN_DEBUG "%s: Host transfer recovered\n",
|
||||
port->name);
|
||||
|
||||
if (time_after_eq (jiffies, expire)) break;
|
||||
goto try_again;
|
||||
success:
|
||||
parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
|
||||
udelay (5);
|
||||
if (parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_BUSY,
|
||||
PARPORT_STATUS_BUSY))
|
||||
/* Peripheral hasn't accepted the data. */
|
||||
break;
|
||||
}
|
||||
|
||||
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
||||
|
||||
return written;
|
||||
#endif /* IEEE1284 support */
|
||||
}
|
||||
|
||||
/* ECP mode, reverse channel, data. */
|
||||
size_t parport_ieee1284_ecp_read_data (struct parport *port,
|
||||
void *buffer, size_t len, int flags)
|
||||
{
|
||||
#ifndef CONFIG_PARPORT_1284
|
||||
return 0;
|
||||
#else
|
||||
struct pardevice *dev = port->cad;
|
||||
unsigned char *buf = buffer;
|
||||
int rle_count = 0; /* shut gcc up */
|
||||
unsigned char ctl;
|
||||
int rle = 0;
|
||||
ssize_t count = 0;
|
||||
|
||||
port = port->physport;
|
||||
|
||||
if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE)
|
||||
if (ecp_forward_to_reverse (port))
|
||||
return 0;
|
||||
|
||||
port->ieee1284.phase = IEEE1284_PH_REV_DATA;
|
||||
|
||||
/* Set HostAck low to start accepting data. */
|
||||
ctl = parport_read_control (port);
|
||||
ctl &= ~(PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT |
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
parport_write_control (port,
|
||||
ctl | PARPORT_CONTROL_AUTOFD);
|
||||
while (count < len) {
|
||||
unsigned long expire = jiffies + dev->timeout;
|
||||
unsigned char byte;
|
||||
int command;
|
||||
|
||||
/* Event 43: Peripheral sets nAck low. It can take as
|
||||
long as it wants. */
|
||||
while (parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0)) {
|
||||
/* The peripheral hasn't given us data in
|
||||
35ms. If we have data to give back to the
|
||||
caller, do it now. */
|
||||
if (count)
|
||||
goto out;
|
||||
|
||||
/* If we've used up all the time we were allowed,
|
||||
give up altogether. */
|
||||
if (!time_before (jiffies, expire))
|
||||
goto out;
|
||||
|
||||
/* Yield the port for a while. */
|
||||
if (count && dev->port->irq != PARPORT_IRQ_NONE) {
|
||||
parport_release (dev);
|
||||
schedule_timeout_interruptible(msecs_to_jiffies(40));
|
||||
parport_claim_or_block (dev);
|
||||
}
|
||||
else
|
||||
/* We must have the device claimed here. */
|
||||
parport_wait_event (port, msecs_to_jiffies(40));
|
||||
|
||||
/* Is there a signal pending? */
|
||||
if (signal_pending (current))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Is this a command? */
|
||||
if (rle)
|
||||
/* The last byte was a run-length count, so
|
||||
this can't be as well. */
|
||||
command = 0;
|
||||
else
|
||||
command = (parport_read_status (port) &
|
||||
PARPORT_STATUS_BUSY) ? 1 : 0;
|
||||
|
||||
/* Read the data. */
|
||||
byte = parport_read_data (port);
|
||||
|
||||
/* If this is a channel command, rather than an RLE
|
||||
command or a normal data byte, don't accept it. */
|
||||
if (command) {
|
||||
if (byte & 0x80) {
|
||||
DPRINTK (KERN_DEBUG "%s: stopping short at "
|
||||
"channel command (%02x)\n",
|
||||
port->name, byte);
|
||||
goto out;
|
||||
}
|
||||
else if (port->ieee1284.mode != IEEE1284_MODE_ECPRLE)
|
||||
DPRINTK (KERN_DEBUG "%s: device illegally "
|
||||
"using RLE; accepting anyway\n",
|
||||
port->name);
|
||||
|
||||
rle_count = byte + 1;
|
||||
|
||||
/* Are we allowed to read that many bytes? */
|
||||
if (rle_count > (len - count)) {
|
||||
DPRINTK (KERN_DEBUG "%s: leaving %d RLE bytes "
|
||||
"for next time\n", port->name,
|
||||
rle_count);
|
||||
break;
|
||||
}
|
||||
|
||||
rle = 1;
|
||||
}
|
||||
|
||||
/* Event 44: Set HostAck high, acknowledging handshake. */
|
||||
parport_write_control (port, ctl);
|
||||
|
||||
/* Event 45: The peripheral has 35ms to set nAck high. */
|
||||
if (parport_wait_peripheral (port, PARPORT_STATUS_ACK,
|
||||
PARPORT_STATUS_ACK)) {
|
||||
/* It's gone wrong. Return what data we have
|
||||
to the caller. */
|
||||
DPRINTK (KERN_DEBUG "ECP read timed out at 45\n");
|
||||
|
||||
if (command)
|
||||
printk (KERN_WARNING
|
||||
"%s: command ignored (%02x)\n",
|
||||
port->name, byte);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Event 46: Set HostAck low and accept the data. */
|
||||
parport_write_control (port,
|
||||
ctl | PARPORT_CONTROL_AUTOFD);
|
||||
|
||||
/* If we just read a run-length count, fetch the data. */
|
||||
if (command)
|
||||
continue;
|
||||
|
||||
/* If this is the byte after a run-length count, decompress. */
|
||||
if (rle) {
|
||||
rle = 0;
|
||||
memset (buf, byte, rle_count);
|
||||
buf += rle_count;
|
||||
count += rle_count;
|
||||
DPRINTK (KERN_DEBUG "%s: decompressed to %d bytes\n",
|
||||
port->name, rle_count);
|
||||
} else {
|
||||
/* Normal data byte. */
|
||||
*buf = byte;
|
||||
buf++, count++;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
|
||||
return count;
|
||||
#endif /* IEEE1284 support */
|
||||
}
|
||||
|
||||
/* ECP mode, forward channel, commands. */
|
||||
size_t parport_ieee1284_ecp_write_addr (struct parport *port,
|
||||
const void *buffer, size_t len,
|
||||
int flags)
|
||||
{
|
||||
#ifndef CONFIG_PARPORT_1284
|
||||
return 0;
|
||||
#else
|
||||
const unsigned char *buf = buffer;
|
||||
size_t written;
|
||||
int retry;
|
||||
|
||||
port = port->physport;
|
||||
|
||||
if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE)
|
||||
if (ecp_reverse_to_forward (port))
|
||||
return 0;
|
||||
|
||||
port->ieee1284.phase = IEEE1284_PH_FWD_DATA;
|
||||
|
||||
/* HostAck low (command, not data) */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_AUTOFD
|
||||
| PARPORT_CONTROL_STROBE
|
||||
| PARPORT_CONTROL_INIT,
|
||||
PARPORT_CONTROL_AUTOFD
|
||||
| PARPORT_CONTROL_INIT);
|
||||
for (written = 0; written < len; written++, buf++) {
|
||||
unsigned long expire = jiffies + port->cad->timeout;
|
||||
unsigned char byte;
|
||||
|
||||
byte = *buf;
|
||||
try_again:
|
||||
parport_write_data (port, byte);
|
||||
parport_frob_control (port, PARPORT_CONTROL_STROBE,
|
||||
PARPORT_CONTROL_STROBE);
|
||||
udelay (5);
|
||||
for (retry = 0; retry < 100; retry++) {
|
||||
if (!parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_BUSY, 0))
|
||||
goto success;
|
||||
|
||||
if (signal_pending (current)) {
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE,
|
||||
0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Time for Host Transfer Recovery (page 41 of IEEE1284) */
|
||||
DPRINTK (KERN_DEBUG "%s: ECP transfer stalled!\n", port->name);
|
||||
|
||||
parport_frob_control (port, PARPORT_CONTROL_INIT,
|
||||
PARPORT_CONTROL_INIT);
|
||||
udelay (50);
|
||||
if (parport_read_status (port) & PARPORT_STATUS_PAPEROUT) {
|
||||
/* It's buggered. */
|
||||
parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
|
||||
udelay (50);
|
||||
if (!(parport_read_status (port) & PARPORT_STATUS_PAPEROUT))
|
||||
break;
|
||||
|
||||
DPRINTK (KERN_DEBUG "%s: Host transfer recovered\n",
|
||||
port->name);
|
||||
|
||||
if (time_after_eq (jiffies, expire)) break;
|
||||
goto try_again;
|
||||
success:
|
||||
parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
|
||||
udelay (5);
|
||||
if (parport_wait_peripheral (port,
|
||||
PARPORT_STATUS_BUSY,
|
||||
PARPORT_STATUS_BUSY))
|
||||
/* Peripheral hasn't accepted the data. */
|
||||
break;
|
||||
}
|
||||
|
||||
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
||||
|
||||
return written;
|
||||
#endif /* IEEE1284 support */
|
||||
}
|
||||
|
||||
/*** *
|
||||
* EPP functions. *
|
||||
* ***/
|
||||
|
||||
/* EPP mode, forward channel, data. */
|
||||
size_t parport_ieee1284_epp_write_data (struct parport *port,
|
||||
const void *buffer, size_t len,
|
||||
int flags)
|
||||
{
|
||||
unsigned char *bp = (unsigned char *) buffer;
|
||||
size_t ret = 0;
|
||||
|
||||
/* set EPP idle state (just to make sure) with strobe low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE |
|
||||
PARPORT_CONTROL_AUTOFD |
|
||||
PARPORT_CONTROL_SELECT |
|
||||
PARPORT_CONTROL_INIT,
|
||||
PARPORT_CONTROL_STROBE |
|
||||
PARPORT_CONTROL_INIT);
|
||||
port->ops->data_forward (port);
|
||||
for (; len > 0; len--, bp++) {
|
||||
/* Event 62: Write data and set autofd low */
|
||||
parport_write_data (port, *bp);
|
||||
parport_frob_control (port, PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
|
||||
/* Event 58: wait for busy (nWait) to go high */
|
||||
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY, 0, 10))
|
||||
break;
|
||||
|
||||
/* Event 63: set nAutoFd (nDStrb) high */
|
||||
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
||||
|
||||
/* Event 60: wait for busy (nWait) to go low */
|
||||
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
|
||||
PARPORT_STATUS_BUSY, 5))
|
||||
break;
|
||||
|
||||
ret++;
|
||||
}
|
||||
|
||||
/* Event 61: set strobe (nWrite) high */
|
||||
parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* EPP mode, reverse channel, data. */
|
||||
size_t parport_ieee1284_epp_read_data (struct parport *port,
|
||||
void *buffer, size_t len,
|
||||
int flags)
|
||||
{
|
||||
unsigned char *bp = (unsigned char *) buffer;
|
||||
unsigned ret = 0;
|
||||
|
||||
/* set EPP idle state (just to make sure) with strobe high */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE |
|
||||
PARPORT_CONTROL_AUTOFD |
|
||||
PARPORT_CONTROL_SELECT |
|
||||
PARPORT_CONTROL_INIT,
|
||||
PARPORT_CONTROL_INIT);
|
||||
port->ops->data_reverse (port);
|
||||
for (; len > 0; len--, bp++) {
|
||||
/* Event 67: set nAutoFd (nDStrb) low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_AUTOFD,
|
||||
PARPORT_CONTROL_AUTOFD);
|
||||
/* Event 58: wait for Busy to go high */
|
||||
if (parport_wait_peripheral (port, PARPORT_STATUS_BUSY, 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
*bp = parport_read_data (port);
|
||||
|
||||
/* Event 63: set nAutoFd (nDStrb) high */
|
||||
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
||||
|
||||
/* Event 60: wait for Busy to go low */
|
||||
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
|
||||
PARPORT_STATUS_BUSY, 5)) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret++;
|
||||
}
|
||||
port->ops->data_forward (port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* EPP mode, forward channel, addresses. */
|
||||
size_t parport_ieee1284_epp_write_addr (struct parport *port,
|
||||
const void *buffer, size_t len,
|
||||
int flags)
|
||||
{
|
||||
unsigned char *bp = (unsigned char *) buffer;
|
||||
size_t ret = 0;
|
||||
|
||||
/* set EPP idle state (just to make sure) with strobe low */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE |
|
||||
PARPORT_CONTROL_AUTOFD |
|
||||
PARPORT_CONTROL_SELECT |
|
||||
PARPORT_CONTROL_INIT,
|
||||
PARPORT_CONTROL_STROBE |
|
||||
PARPORT_CONTROL_INIT);
|
||||
port->ops->data_forward (port);
|
||||
for (; len > 0; len--, bp++) {
|
||||
/* Event 56: Write data and set nAStrb low. */
|
||||
parport_write_data (port, *bp);
|
||||
parport_frob_control (port, PARPORT_CONTROL_SELECT,
|
||||
PARPORT_CONTROL_SELECT);
|
||||
|
||||
/* Event 58: wait for busy (nWait) to go high */
|
||||
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY, 0, 10))
|
||||
break;
|
||||
|
||||
/* Event 59: set nAStrb high */
|
||||
parport_frob_control (port, PARPORT_CONTROL_SELECT, 0);
|
||||
|
||||
/* Event 60: wait for busy (nWait) to go low */
|
||||
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
|
||||
PARPORT_STATUS_BUSY, 5))
|
||||
break;
|
||||
|
||||
ret++;
|
||||
}
|
||||
|
||||
/* Event 61: set strobe (nWrite) high */
|
||||
parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* EPP mode, reverse channel, addresses. */
|
||||
size_t parport_ieee1284_epp_read_addr (struct parport *port,
|
||||
void *buffer, size_t len,
|
||||
int flags)
|
||||
{
|
||||
unsigned char *bp = (unsigned char *) buffer;
|
||||
unsigned ret = 0;
|
||||
|
||||
/* Set EPP idle state (just to make sure) with strobe high */
|
||||
parport_frob_control (port,
|
||||
PARPORT_CONTROL_STROBE |
|
||||
PARPORT_CONTROL_AUTOFD |
|
||||
PARPORT_CONTROL_SELECT |
|
||||
PARPORT_CONTROL_INIT,
|
||||
PARPORT_CONTROL_INIT);
|
||||
port->ops->data_reverse (port);
|
||||
for (; len > 0; len--, bp++) {
|
||||
/* Event 64: set nSelectIn (nAStrb) low */
|
||||
parport_frob_control (port, PARPORT_CONTROL_SELECT,
|
||||
PARPORT_CONTROL_SELECT);
|
||||
|
||||
/* Event 58: wait for Busy to go high */
|
||||
if (parport_wait_peripheral (port, PARPORT_STATUS_BUSY, 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
*bp = parport_read_data (port);
|
||||
|
||||
/* Event 59: set nSelectIn (nAStrb) high */
|
||||
parport_frob_control (port, PARPORT_CONTROL_SELECT,
|
||||
0);
|
||||
|
||||
/* Event 60: wait for Busy to go low */
|
||||
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
|
||||
PARPORT_STATUS_BUSY, 5))
|
||||
break;
|
||||
|
||||
ret++;
|
||||
}
|
||||
port->ops->data_forward (port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(parport_ieee1284_ecp_write_data);
|
||||
EXPORT_SYMBOL(parport_ieee1284_ecp_read_data);
|
||||
EXPORT_SYMBOL(parport_ieee1284_ecp_write_addr);
|
||||
EXPORT_SYMBOL(parport_ieee1284_write_compat);
|
||||
EXPORT_SYMBOL(parport_ieee1284_read_nibble);
|
||||
EXPORT_SYMBOL(parport_ieee1284_read_byte);
|
||||
EXPORT_SYMBOL(parport_ieee1284_epp_write_data);
|
||||
EXPORT_SYMBOL(parport_ieee1284_epp_read_data);
|
||||
EXPORT_SYMBOL(parport_ieee1284_epp_write_addr);
|
||||
EXPORT_SYMBOL(parport_ieee1284_epp_read_addr);
|
20
drivers/parport/multiface.h
Normal file
20
drivers/parport/multiface.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef _MULTIFACE_H_
|
||||
#define _MULTIFACE_H_
|
||||
|
||||
/*
|
||||
* Defines for SerialMaster, Multiface Card II and Multiface Card III
|
||||
* The addresses given below are offsets to the board base address
|
||||
*
|
||||
* 6.11.95 Joerg Dorchain (dorchain@mpi-sb.mpg.de)
|
||||
*
|
||||
*/
|
||||
|
||||
#define PIA_REG_PADWIDTH 255
|
||||
|
||||
#define DUARTBASE 0x0000
|
||||
#define PITBASE 0x0100
|
||||
#define ROMBASE 0x0200
|
||||
#define PIABASE 0x4000
|
||||
|
||||
#endif
|
||||
|
252
drivers/parport/parport_amiga.c
Normal file
252
drivers/parport/parport_amiga.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
/* Low-level parallel port routines for the Amiga built-in port
|
||||
*
|
||||
* Author: Joerg Dorchain <joerg@dorchain.net>
|
||||
*
|
||||
* This is a complete rewrite of the code, but based heaviy upon the old
|
||||
* lp_intern. code.
|
||||
*
|
||||
* The built-in Amiga parallel port provides one port at a fixed address
|
||||
* with 8 bidirectional data lines (D0 - D7) and 3 bidirectional status
|
||||
* lines (BUSY, POUT, SEL), 1 output control line /STROBE (raised automatically
|
||||
* in hardware when the data register is accessed), and 1 input control line
|
||||
* /ACK, able to cause an interrupt, but both not directly settable by
|
||||
* software.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <asm/amigahw.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/amigaints.h>
|
||||
|
||||
#undef DEBUG
|
||||
#ifdef DEBUG
|
||||
#define DPRINTK printk
|
||||
#else
|
||||
#define DPRINTK(x...) do { } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
static void amiga_write_data(struct parport *p, unsigned char data)
|
||||
{
|
||||
DPRINTK(KERN_DEBUG "write_data %c\n",data);
|
||||
/* Triggers also /STROBE. This behavior cannot be changed */
|
||||
ciaa.prb = data;
|
||||
mb();
|
||||
}
|
||||
|
||||
static unsigned char amiga_read_data(struct parport *p)
|
||||
{
|
||||
/* Triggers also /STROBE. This behavior cannot be changed */
|
||||
return ciaa.prb;
|
||||
}
|
||||
|
||||
static unsigned char control_amiga_to_pc(unsigned char control)
|
||||
{
|
||||
return PARPORT_CONTROL_SELECT |
|
||||
PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_STROBE;
|
||||
/* fake value: interrupt enable, select in, no reset,
|
||||
no autolf, no strobe - seems to be closest the wiring diagram */
|
||||
}
|
||||
|
||||
static void amiga_write_control(struct parport *p, unsigned char control)
|
||||
{
|
||||
DPRINTK(KERN_DEBUG "write_control %02x\n",control);
|
||||
/* No implementation possible */
|
||||
}
|
||||
|
||||
static unsigned char amiga_read_control( struct parport *p)
|
||||
{
|
||||
DPRINTK(KERN_DEBUG "read_control \n");
|
||||
return control_amiga_to_pc(0);
|
||||
}
|
||||
|
||||
static unsigned char amiga_frob_control( struct parport *p, unsigned char mask, unsigned char val)
|
||||
{
|
||||
unsigned char old;
|
||||
|
||||
DPRINTK(KERN_DEBUG "frob_control mask %02x, value %02x\n",mask,val);
|
||||
old = amiga_read_control(p);
|
||||
amiga_write_control(p, (old & ~mask) ^ val);
|
||||
return old;
|
||||
}
|
||||
|
||||
static unsigned char status_amiga_to_pc(unsigned char status)
|
||||
{
|
||||
unsigned char ret = PARPORT_STATUS_BUSY | PARPORT_STATUS_ACK | PARPORT_STATUS_ERROR;
|
||||
|
||||
if (status & 1) /* Busy */
|
||||
ret &= ~PARPORT_STATUS_BUSY;
|
||||
if (status & 2) /* PaperOut */
|
||||
ret |= PARPORT_STATUS_PAPEROUT;
|
||||
if (status & 4) /* Selected */
|
||||
ret |= PARPORT_STATUS_SELECT;
|
||||
/* the rest is not connected or handled autonomously in hardware */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned char amiga_read_status(struct parport *p)
|
||||
{
|
||||
unsigned char status;
|
||||
|
||||
status = status_amiga_to_pc(ciab.pra & 7);
|
||||
DPRINTK(KERN_DEBUG "read_status %02x\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void amiga_enable_irq(struct parport *p)
|
||||
{
|
||||
enable_irq(IRQ_AMIGA_CIAA_FLG);
|
||||
}
|
||||
|
||||
static void amiga_disable_irq(struct parport *p)
|
||||
{
|
||||
disable_irq(IRQ_AMIGA_CIAA_FLG);
|
||||
}
|
||||
|
||||
static void amiga_data_forward(struct parport *p)
|
||||
{
|
||||
DPRINTK(KERN_DEBUG "forward\n");
|
||||
ciaa.ddrb = 0xff; /* all pins output */
|
||||
mb();
|
||||
}
|
||||
|
||||
static void amiga_data_reverse(struct parport *p)
|
||||
{
|
||||
DPRINTK(KERN_DEBUG "reverse\n");
|
||||
ciaa.ddrb = 0; /* all pins input */
|
||||
mb();
|
||||
}
|
||||
|
||||
static void amiga_init_state(struct pardevice *dev, struct parport_state *s)
|
||||
{
|
||||
s->u.amiga.data = 0;
|
||||
s->u.amiga.datadir = 255;
|
||||
s->u.amiga.status = 0;
|
||||
s->u.amiga.statusdir = 0;
|
||||
}
|
||||
|
||||
static void amiga_save_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
mb();
|
||||
s->u.amiga.data = ciaa.prb;
|
||||
s->u.amiga.datadir = ciaa.ddrb;
|
||||
s->u.amiga.status = ciab.pra & 7;
|
||||
s->u.amiga.statusdir = ciab.ddra & 7;
|
||||
mb();
|
||||
}
|
||||
|
||||
static void amiga_restore_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
mb();
|
||||
ciaa.prb = s->u.amiga.data;
|
||||
ciaa.ddrb = s->u.amiga.datadir;
|
||||
ciab.pra |= (ciab.pra & 0xf8) | s->u.amiga.status;
|
||||
ciab.ddra |= (ciab.ddra & 0xf8) | s->u.amiga.statusdir;
|
||||
mb();
|
||||
}
|
||||
|
||||
static struct parport_operations pp_amiga_ops = {
|
||||
.write_data = amiga_write_data,
|
||||
.read_data = amiga_read_data,
|
||||
|
||||
.write_control = amiga_write_control,
|
||||
.read_control = amiga_read_control,
|
||||
.frob_control = amiga_frob_control,
|
||||
|
||||
.read_status = amiga_read_status,
|
||||
|
||||
.enable_irq = amiga_enable_irq,
|
||||
.disable_irq = amiga_disable_irq,
|
||||
|
||||
.data_forward = amiga_data_forward,
|
||||
.data_reverse = amiga_data_reverse,
|
||||
|
||||
.init_state = amiga_init_state,
|
||||
.save_state = amiga_save_state,
|
||||
.restore_state = amiga_restore_state,
|
||||
|
||||
.epp_write_data = parport_ieee1284_epp_write_data,
|
||||
.epp_read_data = parport_ieee1284_epp_read_data,
|
||||
.epp_write_addr = parport_ieee1284_epp_write_addr,
|
||||
.epp_read_addr = parport_ieee1284_epp_read_addr,
|
||||
|
||||
.ecp_write_data = parport_ieee1284_ecp_write_data,
|
||||
.ecp_read_data = parport_ieee1284_ecp_read_data,
|
||||
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
|
||||
|
||||
.compat_write_data = parport_ieee1284_write_compat,
|
||||
.nibble_read_data = parport_ieee1284_read_nibble,
|
||||
.byte_read_data = parport_ieee1284_read_byte,
|
||||
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/* ----------- Initialisation code --------------------------------- */
|
||||
|
||||
static int __init amiga_parallel_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct parport *p;
|
||||
int err;
|
||||
|
||||
ciaa.ddrb = 0xff;
|
||||
ciab.ddra &= 0xf8;
|
||||
mb();
|
||||
|
||||
p = parport_register_port((unsigned long)&ciaa.prb, IRQ_AMIGA_CIAA_FLG,
|
||||
PARPORT_DMA_NONE, &pp_amiga_ops);
|
||||
if (!p)
|
||||
return -EBUSY;
|
||||
|
||||
err = request_irq(IRQ_AMIGA_CIAA_FLG, parport_irq_handler, 0, p->name,
|
||||
p);
|
||||
if (err)
|
||||
goto out_irq;
|
||||
|
||||
printk(KERN_INFO "%s: Amiga built-in port using irq\n", p->name);
|
||||
/* XXX: set operating mode */
|
||||
parport_announce_port(p);
|
||||
|
||||
platform_set_drvdata(pdev, p);
|
||||
|
||||
return 0;
|
||||
|
||||
out_irq:
|
||||
parport_put_port(p);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __exit amiga_parallel_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct parport *port = platform_get_drvdata(pdev);
|
||||
|
||||
parport_remove_port(port);
|
||||
if (port->irq != PARPORT_IRQ_NONE)
|
||||
free_irq(IRQ_AMIGA_CIAA_FLG, port);
|
||||
parport_put_port(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver amiga_parallel_driver = {
|
||||
.remove = __exit_p(amiga_parallel_remove),
|
||||
.driver = {
|
||||
.name = "amiga-parallel",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver_probe(amiga_parallel_driver, amiga_parallel_probe);
|
||||
|
||||
MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>");
|
||||
MODULE_DESCRIPTION("Parport Driver for Amiga builtin Port");
|
||||
MODULE_SUPPORTED_DEVICE("Amiga builtin Parallel Port");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:amiga-parallel");
|
224
drivers/parport/parport_atari.c
Normal file
224
drivers/parport/parport_atari.c
Normal file
|
@ -0,0 +1,224 @@
|
|||
/* Low-level parallel port routines for the Atari builtin port
|
||||
*
|
||||
* Author: Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
|
||||
*
|
||||
* Based on parport_amiga.c.
|
||||
*
|
||||
* The built-in Atari parallel port provides one port at a fixed address
|
||||
* with 8 output data lines (D0 - D7), 1 output control line (STROBE)
|
||||
* and 1 input status line (BUSY) able to cause an interrupt.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/atarihw.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/atariints.h>
|
||||
|
||||
static struct parport *this_port = NULL;
|
||||
|
||||
static unsigned char
|
||||
parport_atari_read_data(struct parport *p)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned char data;
|
||||
|
||||
local_irq_save(flags);
|
||||
sound_ym.rd_data_reg_sel = 15;
|
||||
data = sound_ym.rd_data_reg_sel;
|
||||
local_irq_restore(flags);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void
|
||||
parport_atari_write_data(struct parport *p, unsigned char data)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
sound_ym.rd_data_reg_sel = 15;
|
||||
sound_ym.wd_data = data;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
parport_atari_read_control(struct parport *p)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned char control = 0;
|
||||
|
||||
local_irq_save(flags);
|
||||
sound_ym.rd_data_reg_sel = 14;
|
||||
if (!(sound_ym.rd_data_reg_sel & (1 << 5)))
|
||||
control = PARPORT_CONTROL_STROBE;
|
||||
local_irq_restore(flags);
|
||||
return control;
|
||||
}
|
||||
|
||||
static void
|
||||
parport_atari_write_control(struct parport *p, unsigned char control)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
sound_ym.rd_data_reg_sel = 14;
|
||||
if (control & PARPORT_CONTROL_STROBE)
|
||||
sound_ym.wd_data = sound_ym.rd_data_reg_sel & ~(1 << 5);
|
||||
else
|
||||
sound_ym.wd_data = sound_ym.rd_data_reg_sel | (1 << 5);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
parport_atari_frob_control(struct parport *p, unsigned char mask,
|
||||
unsigned char val)
|
||||
{
|
||||
unsigned char old = parport_atari_read_control(p);
|
||||
parport_atari_write_control(p, (old & ~mask) ^ val);
|
||||
return old;
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
parport_atari_read_status(struct parport *p)
|
||||
{
|
||||
return ((st_mfp.par_dt_reg & 1 ? 0 : PARPORT_STATUS_BUSY) |
|
||||
PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_atari_init_state(struct pardevice *d, struct parport_state *s)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
parport_atari_save_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
parport_atari_restore_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
parport_atari_enable_irq(struct parport *p)
|
||||
{
|
||||
enable_irq(IRQ_MFP_BUSY);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_atari_disable_irq(struct parport *p)
|
||||
{
|
||||
disable_irq(IRQ_MFP_BUSY);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_atari_data_forward(struct parport *p)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
/* Soundchip port B as output. */
|
||||
sound_ym.rd_data_reg_sel = 7;
|
||||
sound_ym.wd_data = sound_ym.rd_data_reg_sel | 0x40;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_atari_data_reverse(struct parport *p)
|
||||
{
|
||||
}
|
||||
|
||||
static struct parport_operations parport_atari_ops = {
|
||||
.write_data = parport_atari_write_data,
|
||||
.read_data = parport_atari_read_data,
|
||||
|
||||
.write_control = parport_atari_write_control,
|
||||
.read_control = parport_atari_read_control,
|
||||
.frob_control = parport_atari_frob_control,
|
||||
|
||||
.read_status = parport_atari_read_status,
|
||||
|
||||
.enable_irq = parport_atari_enable_irq,
|
||||
.disable_irq = parport_atari_disable_irq,
|
||||
|
||||
.data_forward = parport_atari_data_forward,
|
||||
.data_reverse = parport_atari_data_reverse,
|
||||
|
||||
.init_state = parport_atari_init_state,
|
||||
.save_state = parport_atari_save_state,
|
||||
.restore_state = parport_atari_restore_state,
|
||||
|
||||
.epp_write_data = parport_ieee1284_epp_write_data,
|
||||
.epp_read_data = parport_ieee1284_epp_read_data,
|
||||
.epp_write_addr = parport_ieee1284_epp_write_addr,
|
||||
.epp_read_addr = parport_ieee1284_epp_read_addr,
|
||||
|
||||
.ecp_write_data = parport_ieee1284_ecp_write_data,
|
||||
.ecp_read_data = parport_ieee1284_ecp_read_data,
|
||||
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
|
||||
|
||||
.compat_write_data = parport_ieee1284_write_compat,
|
||||
.nibble_read_data = parport_ieee1284_read_nibble,
|
||||
.byte_read_data = parport_ieee1284_read_byte,
|
||||
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int __init parport_atari_init(void)
|
||||
{
|
||||
struct parport *p;
|
||||
unsigned long flags;
|
||||
|
||||
if (MACH_IS_ATARI) {
|
||||
local_irq_save(flags);
|
||||
/* Soundchip port A/B as output. */
|
||||
sound_ym.rd_data_reg_sel = 7;
|
||||
sound_ym.wd_data = (sound_ym.rd_data_reg_sel & 0x3f) | 0xc0;
|
||||
/* STROBE high. */
|
||||
sound_ym.rd_data_reg_sel = 14;
|
||||
sound_ym.wd_data = sound_ym.rd_data_reg_sel | (1 << 5);
|
||||
local_irq_restore(flags);
|
||||
/* MFP port I0 as input. */
|
||||
st_mfp.data_dir &= ~1;
|
||||
/* MFP port I0 interrupt on high->low edge. */
|
||||
st_mfp.active_edge &= ~1;
|
||||
p = parport_register_port((unsigned long)&sound_ym.wd_data,
|
||||
IRQ_MFP_BUSY, PARPORT_DMA_NONE,
|
||||
&parport_atari_ops);
|
||||
if (!p)
|
||||
return -ENODEV;
|
||||
if (request_irq(IRQ_MFP_BUSY, parport_irq_handler,
|
||||
IRQ_TYPE_SLOW, p->name, p)) {
|
||||
parport_put_port (p);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
this_port = p;
|
||||
printk(KERN_INFO "%s: Atari built-in port using irq\n", p->name);
|
||||
parport_announce_port (p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void __exit parport_atari_exit(void)
|
||||
{
|
||||
parport_remove_port(this_port);
|
||||
if (this_port->irq != PARPORT_IRQ_NONE)
|
||||
free_irq(IRQ_MFP_BUSY, this_port);
|
||||
parport_put_port(this_port);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Andreas Schwab");
|
||||
MODULE_DESCRIPTION("Parport Driver for Atari builtin Port");
|
||||
MODULE_SUPPORTED_DEVICE("Atari builtin Parallel Port");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(parport_atari_init)
|
||||
module_exit(parport_atari_exit)
|
427
drivers/parport/parport_ax88796.c
Normal file
427
drivers/parport/parport_ax88796.c
Normal file
|
@ -0,0 +1,427 @@
|
|||
/* linux/drivers/parport/parport_ax88796.c
|
||||
*
|
||||
* (c) 2005,2006 Simtec Electronics
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#define AX_SPR_BUSY (1<<7)
|
||||
#define AX_SPR_ACK (1<<6)
|
||||
#define AX_SPR_PE (1<<5)
|
||||
#define AX_SPR_SLCT (1<<4)
|
||||
#define AX_SPR_ERR (1<<3)
|
||||
|
||||
#define AX_CPR_nDOE (1<<5)
|
||||
#define AX_CPR_SLCTIN (1<<3)
|
||||
#define AX_CPR_nINIT (1<<2)
|
||||
#define AX_CPR_ATFD (1<<1)
|
||||
#define AX_CPR_STRB (1<<0)
|
||||
|
||||
struct ax_drvdata {
|
||||
struct parport *parport;
|
||||
struct parport_state suspend;
|
||||
|
||||
struct device *dev;
|
||||
struct resource *io;
|
||||
|
||||
unsigned char irq_enabled;
|
||||
|
||||
void __iomem *base;
|
||||
void __iomem *spp_data;
|
||||
void __iomem *spp_spr;
|
||||
void __iomem *spp_cpr;
|
||||
};
|
||||
|
||||
static inline struct ax_drvdata *pp_to_drv(struct parport *p)
|
||||
{
|
||||
return p->private_data;
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
parport_ax88796_read_data(struct parport *p)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
|
||||
return readb(dd->spp_data);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_ax88796_write_data(struct parport *p, unsigned char data)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
|
||||
writeb(data, dd->spp_data);
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
parport_ax88796_read_control(struct parport *p)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
unsigned int cpr = readb(dd->spp_cpr);
|
||||
unsigned int ret = 0;
|
||||
|
||||
if (!(cpr & AX_CPR_STRB))
|
||||
ret |= PARPORT_CONTROL_STROBE;
|
||||
|
||||
if (!(cpr & AX_CPR_ATFD))
|
||||
ret |= PARPORT_CONTROL_AUTOFD;
|
||||
|
||||
if (cpr & AX_CPR_nINIT)
|
||||
ret |= PARPORT_CONTROL_INIT;
|
||||
|
||||
if (!(cpr & AX_CPR_SLCTIN))
|
||||
ret |= PARPORT_CONTROL_SELECT;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
parport_ax88796_write_control(struct parport *p, unsigned char control)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
unsigned int cpr = readb(dd->spp_cpr);
|
||||
|
||||
cpr &= AX_CPR_nDOE;
|
||||
|
||||
if (!(control & PARPORT_CONTROL_STROBE))
|
||||
cpr |= AX_CPR_STRB;
|
||||
|
||||
if (!(control & PARPORT_CONTROL_AUTOFD))
|
||||
cpr |= AX_CPR_ATFD;
|
||||
|
||||
if (control & PARPORT_CONTROL_INIT)
|
||||
cpr |= AX_CPR_nINIT;
|
||||
|
||||
if (!(control & PARPORT_CONTROL_SELECT))
|
||||
cpr |= AX_CPR_SLCTIN;
|
||||
|
||||
dev_dbg(dd->dev, "write_control: ctrl=%02x, cpr=%02x\n", control, cpr);
|
||||
writeb(cpr, dd->spp_cpr);
|
||||
|
||||
if (parport_ax88796_read_control(p) != control) {
|
||||
dev_err(dd->dev, "write_control: read != set (%02x, %02x)\n",
|
||||
parport_ax88796_read_control(p), control);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
parport_ax88796_read_status(struct parport *p)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
unsigned int status = readb(dd->spp_spr);
|
||||
unsigned int ret = 0;
|
||||
|
||||
if (status & AX_SPR_BUSY)
|
||||
ret |= PARPORT_STATUS_BUSY;
|
||||
|
||||
if (status & AX_SPR_ACK)
|
||||
ret |= PARPORT_STATUS_ACK;
|
||||
|
||||
if (status & AX_SPR_ERR)
|
||||
ret |= PARPORT_STATUS_ERROR;
|
||||
|
||||
if (status & AX_SPR_SLCT)
|
||||
ret |= PARPORT_STATUS_SELECT;
|
||||
|
||||
if (status & AX_SPR_PE)
|
||||
ret |= PARPORT_STATUS_PAPEROUT;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
parport_ax88796_frob_control(struct parport *p, unsigned char mask,
|
||||
unsigned char val)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
unsigned char old = parport_ax88796_read_control(p);
|
||||
|
||||
dev_dbg(dd->dev, "frob: mask=%02x, val=%02x, old=%02x\n",
|
||||
mask, val, old);
|
||||
|
||||
parport_ax88796_write_control(p, (old & ~mask) | val);
|
||||
return old;
|
||||
}
|
||||
|
||||
static void
|
||||
parport_ax88796_enable_irq(struct parport *p)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
if (!dd->irq_enabled) {
|
||||
enable_irq(p->irq);
|
||||
dd->irq_enabled = 1;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_ax88796_disable_irq(struct parport *p)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
if (dd->irq_enabled) {
|
||||
disable_irq(p->irq);
|
||||
dd->irq_enabled = 0;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_ax88796_data_forward(struct parport *p)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
void __iomem *cpr = dd->spp_cpr;
|
||||
|
||||
writeb((readb(cpr) & ~AX_CPR_nDOE), cpr);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_ax88796_data_reverse(struct parport *p)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
void __iomem *cpr = dd->spp_cpr;
|
||||
|
||||
writeb(readb(cpr) | AX_CPR_nDOE, cpr);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_ax88796_init_state(struct pardevice *d, struct parport_state *s)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(d->port);
|
||||
|
||||
memset(s, 0, sizeof(struct parport_state));
|
||||
|
||||
dev_dbg(dd->dev, "init_state: %p: state=%p\n", d, s);
|
||||
s->u.ax88796.cpr = readb(dd->spp_cpr);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_ax88796_save_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
|
||||
dev_dbg(dd->dev, "save_state: %p: state=%p\n", p, s);
|
||||
s->u.ax88796.cpr = readb(dd->spp_cpr);
|
||||
}
|
||||
|
||||
static void
|
||||
parport_ax88796_restore_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
|
||||
dev_dbg(dd->dev, "restore_state: %p: state=%p\n", p, s);
|
||||
writeb(s->u.ax88796.cpr, dd->spp_cpr);
|
||||
}
|
||||
|
||||
static struct parport_operations parport_ax88796_ops = {
|
||||
.write_data = parport_ax88796_write_data,
|
||||
.read_data = parport_ax88796_read_data,
|
||||
|
||||
.write_control = parport_ax88796_write_control,
|
||||
.read_control = parport_ax88796_read_control,
|
||||
.frob_control = parport_ax88796_frob_control,
|
||||
|
||||
.read_status = parport_ax88796_read_status,
|
||||
|
||||
.enable_irq = parport_ax88796_enable_irq,
|
||||
.disable_irq = parport_ax88796_disable_irq,
|
||||
|
||||
.data_forward = parport_ax88796_data_forward,
|
||||
.data_reverse = parport_ax88796_data_reverse,
|
||||
|
||||
.init_state = parport_ax88796_init_state,
|
||||
.save_state = parport_ax88796_save_state,
|
||||
.restore_state = parport_ax88796_restore_state,
|
||||
|
||||
.epp_write_data = parport_ieee1284_epp_write_data,
|
||||
.epp_read_data = parport_ieee1284_epp_read_data,
|
||||
.epp_write_addr = parport_ieee1284_epp_write_addr,
|
||||
.epp_read_addr = parport_ieee1284_epp_read_addr,
|
||||
|
||||
.ecp_write_data = parport_ieee1284_ecp_write_data,
|
||||
.ecp_read_data = parport_ieee1284_ecp_read_data,
|
||||
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
|
||||
|
||||
.compat_write_data = parport_ieee1284_write_compat,
|
||||
.nibble_read_data = parport_ieee1284_read_nibble,
|
||||
.byte_read_data = parport_ieee1284_read_byte,
|
||||
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int parport_ax88796_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *_dev = &pdev->dev;
|
||||
struct ax_drvdata *dd;
|
||||
struct parport *pp = NULL;
|
||||
struct resource *res;
|
||||
unsigned long size;
|
||||
int spacing;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
dd = kzalloc(sizeof(struct ax_drvdata), GFP_KERNEL);
|
||||
if (dd == NULL) {
|
||||
dev_err(_dev, "no memory for private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
dev_err(_dev, "no MEM specified\n");
|
||||
ret = -ENXIO;
|
||||
goto exit_mem;
|
||||
}
|
||||
|
||||
size = resource_size(res);
|
||||
spacing = size / 3;
|
||||
|
||||
dd->io = request_mem_region(res->start, size, pdev->name);
|
||||
if (dd->io == NULL) {
|
||||
dev_err(_dev, "cannot reserve memory\n");
|
||||
ret = -ENXIO;
|
||||
goto exit_mem;
|
||||
}
|
||||
|
||||
dd->base = ioremap(res->start, size);
|
||||
if (dd->base == NULL) {
|
||||
dev_err(_dev, "cannot ioremap region\n");
|
||||
ret = -ENXIO;
|
||||
goto exit_res;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0)
|
||||
irq = PARPORT_IRQ_NONE;
|
||||
|
||||
pp = parport_register_port((unsigned long)dd->base, irq,
|
||||
PARPORT_DMA_NONE,
|
||||
&parport_ax88796_ops);
|
||||
|
||||
if (pp == NULL) {
|
||||
dev_err(_dev, "failed to register parallel port\n");
|
||||
ret = -ENOMEM;
|
||||
goto exit_unmap;
|
||||
}
|
||||
|
||||
pp->private_data = dd;
|
||||
dd->parport = pp;
|
||||
dd->dev = _dev;
|
||||
|
||||
dd->spp_data = dd->base;
|
||||
dd->spp_spr = dd->base + (spacing * 1);
|
||||
dd->spp_cpr = dd->base + (spacing * 2);
|
||||
|
||||
/* initialise the port controls */
|
||||
writeb(AX_CPR_STRB, dd->spp_cpr);
|
||||
|
||||
if (irq >= 0) {
|
||||
/* request irq */
|
||||
ret = request_irq(irq, parport_irq_handler,
|
||||
IRQF_TRIGGER_FALLING, pdev->name, pp);
|
||||
|
||||
if (ret < 0)
|
||||
goto exit_port;
|
||||
|
||||
dd->irq_enabled = 1;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, pp);
|
||||
|
||||
dev_info(_dev, "attached parallel port driver\n");
|
||||
parport_announce_port(pp);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_port:
|
||||
parport_remove_port(pp);
|
||||
exit_unmap:
|
||||
iounmap(dd->base);
|
||||
exit_res:
|
||||
release_resource(dd->io);
|
||||
kfree(dd->io);
|
||||
exit_mem:
|
||||
kfree(dd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parport_ax88796_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct parport *p = platform_get_drvdata(pdev);
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
|
||||
free_irq(p->irq, p);
|
||||
parport_remove_port(p);
|
||||
iounmap(dd->base);
|
||||
release_resource(dd->io);
|
||||
kfree(dd->io);
|
||||
kfree(dd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int parport_ax88796_suspend(struct platform_device *dev,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct parport *p = platform_get_drvdata(dev);
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
|
||||
parport_ax88796_save_state(p, &dd->suspend);
|
||||
writeb(AX_CPR_nDOE | AX_CPR_STRB, dd->spp_cpr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parport_ax88796_resume(struct platform_device *dev)
|
||||
{
|
||||
struct parport *p = platform_get_drvdata(dev);
|
||||
struct ax_drvdata *dd = pp_to_drv(p);
|
||||
|
||||
parport_ax88796_restore_state(p, &dd->suspend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define parport_ax88796_suspend NULL
|
||||
#define parport_ax88796_resume NULL
|
||||
#endif
|
||||
|
||||
MODULE_ALIAS("platform:ax88796-pp");
|
||||
|
||||
static struct platform_driver axdrv = {
|
||||
.driver = {
|
||||
.name = "ax88796-pp",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = parport_ax88796_probe,
|
||||
.remove = parport_ax88796_remove,
|
||||
.suspend = parport_ax88796_suspend,
|
||||
.resume = parport_ax88796_resume,
|
||||
};
|
||||
|
||||
module_platform_driver(axdrv);
|
||||
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("AX88796 Parport parallel port driver");
|
||||
MODULE_LICENSE("GPL");
|
196
drivers/parport/parport_cs.c
Normal file
196
drivers/parport/parport_cs.c
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*======================================================================
|
||||
|
||||
A driver for PCMCIA parallel port adapters
|
||||
|
||||
(specifically, for the Quatech SPP-100 EPP card: other cards will
|
||||
probably require driver tweaks)
|
||||
|
||||
parport_cs.c 1.29 2002/10/11 06:57:41
|
||||
|
||||
The contents of this file are subject to the Mozilla Public
|
||||
License Version 1.1 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a copy of
|
||||
the License at http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS
|
||||
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
rights and limitations under the License.
|
||||
|
||||
The initial developer of the original code is David A. Hinds
|
||||
<dahinds@users.sourceforge.net>. Portions created by David A. Hinds
|
||||
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
|
||||
Alternatively, the contents of this file may be used under the
|
||||
terms of the GNU General Public License version 2 (the "GPL"), in
|
||||
which case the provisions of the GPL are applicable instead of the
|
||||
above. If you wish to allow the use of your version of this file
|
||||
only under the terms of the GPL and not to allow others to use
|
||||
your version of this file under the MPL, indicate your decision
|
||||
by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this
|
||||
file under either the MPL or the GPL.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/parport.h>
|
||||
#include <linux/parport_pc.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ds.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
#include <pcmcia/ciscode.h>
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
/* Module parameters */
|
||||
|
||||
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
|
||||
MODULE_DESCRIPTION("PCMCIA parallel port card driver");
|
||||
MODULE_LICENSE("Dual MPL/GPL");
|
||||
|
||||
#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
|
||||
|
||||
INT_MODULE_PARM(epp_mode, 1);
|
||||
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
#define FORCE_EPP_MODE 0x08
|
||||
|
||||
typedef struct parport_info_t {
|
||||
struct pcmcia_device *p_dev;
|
||||
int ndev;
|
||||
struct parport *port;
|
||||
} parport_info_t;
|
||||
|
||||
static void parport_detach(struct pcmcia_device *p_dev);
|
||||
static int parport_config(struct pcmcia_device *link);
|
||||
static void parport_cs_release(struct pcmcia_device *);
|
||||
|
||||
static int parport_probe(struct pcmcia_device *link)
|
||||
{
|
||||
parport_info_t *info;
|
||||
|
||||
dev_dbg(&link->dev, "parport_attach()\n");
|
||||
|
||||
/* Create new parport device */
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) return -ENOMEM;
|
||||
link->priv = info;
|
||||
info->p_dev = link;
|
||||
|
||||
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
|
||||
|
||||
return parport_config(link);
|
||||
} /* parport_attach */
|
||||
|
||||
static void parport_detach(struct pcmcia_device *link)
|
||||
{
|
||||
dev_dbg(&link->dev, "parport_detach\n");
|
||||
|
||||
parport_cs_release(link);
|
||||
|
||||
kfree(link->priv);
|
||||
} /* parport_detach */
|
||||
|
||||
static int parport_config_check(struct pcmcia_device *p_dev, void *priv_data)
|
||||
{
|
||||
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
|
||||
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
|
||||
p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
|
||||
p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
|
||||
|
||||
return pcmcia_request_io(p_dev);
|
||||
}
|
||||
|
||||
static int parport_config(struct pcmcia_device *link)
|
||||
{
|
||||
parport_info_t *info = link->priv;
|
||||
struct parport *p;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&link->dev, "parport_config\n");
|
||||
|
||||
if (epp_mode)
|
||||
link->config_index |= FORCE_EPP_MODE;
|
||||
|
||||
ret = pcmcia_loop_config(link, parport_config_check, NULL);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
if (!link->irq)
|
||||
goto failed;
|
||||
ret = pcmcia_enable_device(link);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
p = parport_pc_probe_port(link->resource[0]->start,
|
||||
link->resource[1]->start,
|
||||
link->irq, PARPORT_DMA_NONE,
|
||||
&link->dev, IRQF_SHARED);
|
||||
if (p == NULL) {
|
||||
printk(KERN_NOTICE "parport_cs: parport_pc_probe_port() at "
|
||||
"0x%3x, irq %u failed\n",
|
||||
(unsigned int) link->resource[0]->start,
|
||||
link->irq);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
p->modes |= PARPORT_MODE_PCSPP;
|
||||
if (epp_mode)
|
||||
p->modes |= PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP;
|
||||
info->ndev = 1;
|
||||
info->port = p;
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
parport_cs_release(link);
|
||||
return -ENODEV;
|
||||
} /* parport_config */
|
||||
|
||||
static void parport_cs_release(struct pcmcia_device *link)
|
||||
{
|
||||
parport_info_t *info = link->priv;
|
||||
|
||||
dev_dbg(&link->dev, "parport_release\n");
|
||||
|
||||
if (info->ndev) {
|
||||
struct parport *p = info->port;
|
||||
parport_pc_unregister_port(p);
|
||||
}
|
||||
info->ndev = 0;
|
||||
|
||||
pcmcia_disable_device(link);
|
||||
} /* parport_cs_release */
|
||||
|
||||
|
||||
static const struct pcmcia_device_id parport_ids[] = {
|
||||
PCMCIA_DEVICE_FUNC_ID(3),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial+Parallel Port: SP230",0x3beb8cf2,0xdb9e58bc),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0003),
|
||||
PCMCIA_DEVICE_NULL
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, parport_ids);
|
||||
|
||||
static struct pcmcia_driver parport_cs_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "parport_cs",
|
||||
.probe = parport_probe,
|
||||
.remove = parport_detach,
|
||||
.id_table = parport_ids,
|
||||
};
|
||||
module_pcmcia_driver(parport_cs_driver);
|
431
drivers/parport/parport_gsc.c
Normal file
431
drivers/parport/parport_gsc.c
Normal file
|
@ -0,0 +1,431 @@
|
|||
/*
|
||||
* Low-level parallel-support for PC-style hardware integrated in the
|
||||
* LASI-Controller (on GSC-Bus) for HP-PARISC Workstations
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* (C) 1999-2001 by Helge Deller <deller@gmx.de>
|
||||
*
|
||||
*
|
||||
* based on parport_pc.c by
|
||||
* Grant Guenther <grant@torque.net>
|
||||
* Phil Blundell <philb@gnu.org>
|
||||
* Tim Waugh <tim@cyberelk.demon.co.uk>
|
||||
* Jose Renau <renau@acm.org>
|
||||
* David Campbell
|
||||
* Andrea Arcangeli
|
||||
*/
|
||||
|
||||
#undef DEBUG /* undef for production */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/superio.h>
|
||||
|
||||
#include <linux/parport.h>
|
||||
#include <asm/pdc.h>
|
||||
#include <asm/parisc-device.h>
|
||||
#include <asm/hardware.h>
|
||||
#include "parport_gsc.h"
|
||||
|
||||
|
||||
MODULE_AUTHOR("Helge Deller <deller@gmx.de>");
|
||||
MODULE_DESCRIPTION("HP-PARISC PC-style parallel port driver");
|
||||
MODULE_SUPPORTED_DEVICE("integrated PC-style parallel port");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
/*
|
||||
* Clear TIMEOUT BIT in EPP MODE
|
||||
*
|
||||
* This is also used in SPP detection.
|
||||
*/
|
||||
static int clear_epp_timeout(struct parport *pb)
|
||||
{
|
||||
unsigned char r;
|
||||
|
||||
if (!(parport_gsc_read_status(pb) & 0x01))
|
||||
return 1;
|
||||
|
||||
/* To clear timeout some chips require double read */
|
||||
parport_gsc_read_status(pb);
|
||||
r = parport_gsc_read_status(pb);
|
||||
parport_writeb (r | 0x01, STATUS (pb)); /* Some reset by writing 1 */
|
||||
parport_writeb (r & 0xfe, STATUS (pb)); /* Others by writing 0 */
|
||||
r = parport_gsc_read_status(pb);
|
||||
|
||||
return !(r & 0x01);
|
||||
}
|
||||
|
||||
/*
|
||||
* Access functions.
|
||||
*
|
||||
* Most of these aren't static because they may be used by the
|
||||
* parport_xxx_yyy macros. extern __inline__ versions of several
|
||||
* of these are in parport_gsc.h.
|
||||
*/
|
||||
|
||||
void parport_gsc_init_state(struct pardevice *dev, struct parport_state *s)
|
||||
{
|
||||
s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
|
||||
}
|
||||
|
||||
void parport_gsc_save_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
s->u.pc.ctr = parport_readb (CONTROL (p));
|
||||
}
|
||||
|
||||
void parport_gsc_restore_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
parport_writeb (s->u.pc.ctr, CONTROL (p));
|
||||
}
|
||||
|
||||
struct parport_operations parport_gsc_ops =
|
||||
{
|
||||
.write_data = parport_gsc_write_data,
|
||||
.read_data = parport_gsc_read_data,
|
||||
|
||||
.write_control = parport_gsc_write_control,
|
||||
.read_control = parport_gsc_read_control,
|
||||
.frob_control = parport_gsc_frob_control,
|
||||
|
||||
.read_status = parport_gsc_read_status,
|
||||
|
||||
.enable_irq = parport_gsc_enable_irq,
|
||||
.disable_irq = parport_gsc_disable_irq,
|
||||
|
||||
.data_forward = parport_gsc_data_forward,
|
||||
.data_reverse = parport_gsc_data_reverse,
|
||||
|
||||
.init_state = parport_gsc_init_state,
|
||||
.save_state = parport_gsc_save_state,
|
||||
.restore_state = parport_gsc_restore_state,
|
||||
|
||||
.epp_write_data = parport_ieee1284_epp_write_data,
|
||||
.epp_read_data = parport_ieee1284_epp_read_data,
|
||||
.epp_write_addr = parport_ieee1284_epp_write_addr,
|
||||
.epp_read_addr = parport_ieee1284_epp_read_addr,
|
||||
|
||||
.ecp_write_data = parport_ieee1284_ecp_write_data,
|
||||
.ecp_read_data = parport_ieee1284_ecp_read_data,
|
||||
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
|
||||
|
||||
.compat_write_data = parport_ieee1284_write_compat,
|
||||
.nibble_read_data = parport_ieee1284_read_nibble,
|
||||
.byte_read_data = parport_ieee1284_read_byte,
|
||||
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/* --- Mode detection ------------------------------------- */
|
||||
|
||||
/*
|
||||
* Checks for port existence, all ports support SPP MODE
|
||||
*/
|
||||
static int parport_SPP_supported(struct parport *pb)
|
||||
{
|
||||
unsigned char r, w;
|
||||
|
||||
/*
|
||||
* first clear an eventually pending EPP timeout
|
||||
* I (sailer@ife.ee.ethz.ch) have an SMSC chipset
|
||||
* that does not even respond to SPP cycles if an EPP
|
||||
* timeout is pending
|
||||
*/
|
||||
clear_epp_timeout(pb);
|
||||
|
||||
/* Do a simple read-write test to make sure the port exists. */
|
||||
w = 0xc;
|
||||
parport_writeb (w, CONTROL (pb));
|
||||
|
||||
/* Is there a control register that we can read from? Some
|
||||
* ports don't allow reads, so read_control just returns a
|
||||
* software copy. Some ports _do_ allow reads, so bypass the
|
||||
* software copy here. In addition, some bits aren't
|
||||
* writable. */
|
||||
r = parport_readb (CONTROL (pb));
|
||||
if ((r & 0xf) == w) {
|
||||
w = 0xe;
|
||||
parport_writeb (w, CONTROL (pb));
|
||||
r = parport_readb (CONTROL (pb));
|
||||
parport_writeb (0xc, CONTROL (pb));
|
||||
if ((r & 0xf) == w)
|
||||
return PARPORT_MODE_PCSPP;
|
||||
}
|
||||
|
||||
/* Try the data register. The data lines aren't tri-stated at
|
||||
* this stage, so we expect back what we wrote. */
|
||||
w = 0xaa;
|
||||
parport_gsc_write_data (pb, w);
|
||||
r = parport_gsc_read_data (pb);
|
||||
if (r == w) {
|
||||
w = 0x55;
|
||||
parport_gsc_write_data (pb, w);
|
||||
r = parport_gsc_read_data (pb);
|
||||
if (r == w)
|
||||
return PARPORT_MODE_PCSPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Detect PS/2 support.
|
||||
*
|
||||
* Bit 5 (0x20) sets the PS/2 data direction; setting this high
|
||||
* allows us to read data from the data lines. In theory we would get back
|
||||
* 0xff but any peripheral attached to the port may drag some or all of the
|
||||
* lines down to zero. So if we get back anything that isn't the contents
|
||||
* of the data register we deem PS/2 support to be present.
|
||||
*
|
||||
* Some SPP ports have "half PS/2" ability - you can't turn off the line
|
||||
* drivers, but an external peripheral with sufficiently beefy drivers of
|
||||
* its own can overpower them and assert its own levels onto the bus, from
|
||||
* where they can then be read back as normal. Ports with this property
|
||||
* and the right type of device attached are likely to fail the SPP test,
|
||||
* (as they will appear to have stuck bits) and so the fact that they might
|
||||
* be misdetected here is rather academic.
|
||||
*/
|
||||
|
||||
static int parport_PS2_supported(struct parport *pb)
|
||||
{
|
||||
int ok = 0;
|
||||
|
||||
clear_epp_timeout(pb);
|
||||
|
||||
/* try to tri-state the buffer */
|
||||
parport_gsc_data_reverse (pb);
|
||||
|
||||
parport_gsc_write_data(pb, 0x55);
|
||||
if (parport_gsc_read_data(pb) != 0x55) ok++;
|
||||
|
||||
parport_gsc_write_data(pb, 0xaa);
|
||||
if (parport_gsc_read_data(pb) != 0xaa) ok++;
|
||||
|
||||
/* cancel input mode */
|
||||
parport_gsc_data_forward (pb);
|
||||
|
||||
if (ok) {
|
||||
pb->modes |= PARPORT_MODE_TRISTATE;
|
||||
} else {
|
||||
struct parport_gsc_private *priv = pb->private_data;
|
||||
priv->ctr_writable &= ~0x20;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
/* --- Initialisation code -------------------------------- */
|
||||
|
||||
struct parport *parport_gsc_probe_port(unsigned long base,
|
||||
unsigned long base_hi, int irq,
|
||||
int dma, struct parisc_device *padev)
|
||||
{
|
||||
struct parport_gsc_private *priv;
|
||||
struct parport_operations *ops;
|
||||
struct parport tmp;
|
||||
struct parport *p = &tmp;
|
||||
|
||||
priv = kzalloc (sizeof (struct parport_gsc_private), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base);
|
||||
return NULL;
|
||||
}
|
||||
ops = kmemdup(&parport_gsc_ops, sizeof(struct parport_operations),
|
||||
GFP_KERNEL);
|
||||
if (!ops) {
|
||||
printk (KERN_DEBUG "parport (0x%lx): no memory for ops!\n",
|
||||
base);
|
||||
kfree (priv);
|
||||
return NULL;
|
||||
}
|
||||
priv->ctr = 0xc;
|
||||
priv->ctr_writable = 0xff;
|
||||
priv->dma_buf = 0;
|
||||
priv->dma_handle = 0;
|
||||
p->base = base;
|
||||
p->base_hi = base_hi;
|
||||
p->irq = irq;
|
||||
p->dma = dma;
|
||||
p->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT;
|
||||
p->ops = ops;
|
||||
p->private_data = priv;
|
||||
p->physport = p;
|
||||
if (!parport_SPP_supported (p)) {
|
||||
/* No port. */
|
||||
kfree (priv);
|
||||
kfree(ops);
|
||||
return NULL;
|
||||
}
|
||||
parport_PS2_supported (p);
|
||||
|
||||
if (!(p = parport_register_port(base, PARPORT_IRQ_NONE,
|
||||
PARPORT_DMA_NONE, ops))) {
|
||||
kfree (priv);
|
||||
kfree (ops);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p->dev = &padev->dev;
|
||||
p->base_hi = base_hi;
|
||||
p->modes = tmp.modes;
|
||||
p->size = (p->modes & PARPORT_MODE_EPP)?8:3;
|
||||
p->private_data = priv;
|
||||
|
||||
printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
|
||||
p->irq = irq;
|
||||
if (p->irq == PARPORT_IRQ_AUTO) {
|
||||
p->irq = PARPORT_IRQ_NONE;
|
||||
}
|
||||
if (p->irq != PARPORT_IRQ_NONE) {
|
||||
printk(", irq %d", p->irq);
|
||||
|
||||
if (p->dma == PARPORT_DMA_AUTO) {
|
||||
p->dma = PARPORT_DMA_NONE;
|
||||
}
|
||||
}
|
||||
if (p->dma == PARPORT_DMA_AUTO) /* To use DMA, giving the irq
|
||||
is mandatory (see above) */
|
||||
p->dma = PARPORT_DMA_NONE;
|
||||
|
||||
printk(" [");
|
||||
#define printmode(x) {if(p->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
|
||||
{
|
||||
int f = 0;
|
||||
printmode(PCSPP);
|
||||
printmode(TRISTATE);
|
||||
printmode(COMPAT)
|
||||
printmode(EPP);
|
||||
// printmode(ECP);
|
||||
// printmode(DMA);
|
||||
}
|
||||
#undef printmode
|
||||
printk("]\n");
|
||||
|
||||
if (p->irq != PARPORT_IRQ_NONE) {
|
||||
if (request_irq (p->irq, parport_irq_handler,
|
||||
0, p->name, p)) {
|
||||
printk (KERN_WARNING "%s: irq %d in use, "
|
||||
"resorting to polled operation\n",
|
||||
p->name, p->irq);
|
||||
p->irq = PARPORT_IRQ_NONE;
|
||||
p->dma = PARPORT_DMA_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Done probing. Now put the port into a sensible start-up state. */
|
||||
|
||||
parport_gsc_write_data(p, 0);
|
||||
parport_gsc_data_forward (p);
|
||||
|
||||
/* Now that we've told the sharing engine about the port, and
|
||||
found out its characteristics, let the high-level drivers
|
||||
know about it. */
|
||||
parport_announce_port (p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
#define PARPORT_GSC_OFFSET 0x800
|
||||
|
||||
static int parport_count;
|
||||
|
||||
static int parport_init_chip(struct parisc_device *dev)
|
||||
{
|
||||
struct parport *p;
|
||||
unsigned long port;
|
||||
|
||||
if (!dev->irq) {
|
||||
printk(KERN_WARNING "IRQ not found for parallel device at 0x%llx\n",
|
||||
(unsigned long long)dev->hpa.start);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
port = dev->hpa.start + PARPORT_GSC_OFFSET;
|
||||
|
||||
/* some older machines with ASP-chip don't support
|
||||
* the enhanced parport modes.
|
||||
*/
|
||||
if (boot_cpu_data.cpu_type > pcxt && !pdc_add_valid(port+4)) {
|
||||
|
||||
/* Initialize bidirectional-mode (0x10) & data-tranfer-mode #1 (0x20) */
|
||||
printk("%s: initialize bidirectional-mode.\n", __func__);
|
||||
parport_writeb ( (0x10 + 0x20), port + 4);
|
||||
|
||||
} else {
|
||||
printk("%s: enhanced parport-modes not supported.\n", __func__);
|
||||
}
|
||||
|
||||
p = parport_gsc_probe_port(port, 0, dev->irq,
|
||||
/* PARPORT_IRQ_NONE */ PARPORT_DMA_NONE, dev);
|
||||
if (p)
|
||||
parport_count++;
|
||||
dev_set_drvdata(&dev->dev, p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parport_remove_chip(struct parisc_device *dev)
|
||||
{
|
||||
struct parport *p = dev_get_drvdata(&dev->dev);
|
||||
if (p) {
|
||||
struct parport_gsc_private *priv = p->private_data;
|
||||
struct parport_operations *ops = p->ops;
|
||||
parport_remove_port(p);
|
||||
if (p->dma != PARPORT_DMA_NONE)
|
||||
free_dma(p->dma);
|
||||
if (p->irq != PARPORT_IRQ_NONE)
|
||||
free_irq(p->irq, p);
|
||||
if (priv->dma_buf)
|
||||
pci_free_consistent(priv->dev, PAGE_SIZE,
|
||||
priv->dma_buf,
|
||||
priv->dma_handle);
|
||||
kfree (p->private_data);
|
||||
parport_put_port(p);
|
||||
kfree (ops); /* hope no-one cached it */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct parisc_device_id parport_tbl[] = {
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x74 },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(parisc, parport_tbl);
|
||||
|
||||
static struct parisc_driver parport_driver = {
|
||||
.name = "Parallel",
|
||||
.id_table = parport_tbl,
|
||||
.probe = parport_init_chip,
|
||||
.remove = parport_remove_chip,
|
||||
};
|
||||
|
||||
int parport_gsc_init(void)
|
||||
{
|
||||
return register_parisc_driver(&parport_driver);
|
||||
}
|
||||
|
||||
static void parport_gsc_exit(void)
|
||||
{
|
||||
unregister_parisc_driver(&parport_driver);
|
||||
}
|
||||
|
||||
module_init(parport_gsc_init);
|
||||
module_exit(parport_gsc_exit);
|
222
drivers/parport/parport_gsc.h
Normal file
222
drivers/parport/parport_gsc.h
Normal file
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Low-level parallel-support for PC-style hardware integrated in the
|
||||
* LASI-Controller (on GSC-Bus) for HP-PARISC Workstations
|
||||
*
|
||||
* (C) 1999-2001 by Helge Deller <deller@gmx.de>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* based on parport_pc.c by
|
||||
* Grant Guenther <grant@torque.net>
|
||||
* Phil Blundell <Philip.Blundell@pobox.com>
|
||||
* Tim Waugh <tim@cyberelk.demon.co.uk>
|
||||
* Jose Renau <renau@acm.org>
|
||||
* David Campbell
|
||||
* Andrea Arcangeli
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_PARPORT_PARPORT_GSC_H
|
||||
#define __DRIVERS_PARPORT_PARPORT_GSC_H
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#undef DEBUG_PARPORT /* undefine for production */
|
||||
#define DELAY_TIME 0
|
||||
|
||||
#if DELAY_TIME == 0
|
||||
#define parport_readb gsc_readb
|
||||
#define parport_writeb gsc_writeb
|
||||
#else
|
||||
static __inline__ unsigned char parport_readb( unsigned long port )
|
||||
{
|
||||
udelay(DELAY_TIME);
|
||||
return gsc_readb(port);
|
||||
}
|
||||
|
||||
static __inline__ void parport_writeb( unsigned char value, unsigned long port )
|
||||
{
|
||||
gsc_writeb(value,port);
|
||||
udelay(DELAY_TIME);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* --- register definitions ------------------------------- */
|
||||
|
||||
#define EPPDATA(p) ((p)->base + 0x4)
|
||||
#define EPPADDR(p) ((p)->base + 0x3)
|
||||
#define CONTROL(p) ((p)->base + 0x2)
|
||||
#define STATUS(p) ((p)->base + 0x1)
|
||||
#define DATA(p) ((p)->base + 0x0)
|
||||
|
||||
struct parport_gsc_private {
|
||||
/* Contents of CTR. */
|
||||
unsigned char ctr;
|
||||
|
||||
/* Bitmask of writable CTR bits. */
|
||||
unsigned char ctr_writable;
|
||||
|
||||
/* Number of bytes per portword. */
|
||||
int pword;
|
||||
|
||||
/* Not used yet. */
|
||||
int readIntrThreshold;
|
||||
int writeIntrThreshold;
|
||||
|
||||
/* buffer suitable for DMA, if DMA enabled */
|
||||
char *dma_buf;
|
||||
dma_addr_t dma_handle;
|
||||
struct pci_dev *dev;
|
||||
};
|
||||
|
||||
static inline void parport_gsc_write_data(struct parport *p, unsigned char d)
|
||||
{
|
||||
#ifdef DEBUG_PARPORT
|
||||
printk (KERN_DEBUG "parport_gsc_write_data(%p,0x%02x)\n", p, d);
|
||||
#endif
|
||||
parport_writeb(d, DATA(p));
|
||||
}
|
||||
|
||||
static inline unsigned char parport_gsc_read_data(struct parport *p)
|
||||
{
|
||||
unsigned char val = parport_readb (DATA (p));
|
||||
#ifdef DEBUG_PARPORT
|
||||
printk (KERN_DEBUG "parport_gsc_read_data(%p) = 0x%02x\n",
|
||||
p, val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
/* __parport_gsc_frob_control differs from parport_gsc_frob_control in that
|
||||
* it doesn't do any extra masking. */
|
||||
static inline unsigned char __parport_gsc_frob_control(struct parport *p,
|
||||
unsigned char mask,
|
||||
unsigned char val)
|
||||
{
|
||||
struct parport_gsc_private *priv = p->physport->private_data;
|
||||
unsigned char ctr = priv->ctr;
|
||||
#ifdef DEBUG_PARPORT
|
||||
printk (KERN_DEBUG
|
||||
"__parport_gsc_frob_control(%02x,%02x): %02x -> %02x\n",
|
||||
mask, val, ctr, ((ctr & ~mask) ^ val) & priv->ctr_writable);
|
||||
#endif
|
||||
ctr = (ctr & ~mask) ^ val;
|
||||
ctr &= priv->ctr_writable; /* only write writable bits. */
|
||||
parport_writeb (ctr, CONTROL (p));
|
||||
priv->ctr = ctr; /* Update soft copy */
|
||||
return ctr;
|
||||
}
|
||||
|
||||
static inline void parport_gsc_data_reverse(struct parport *p)
|
||||
{
|
||||
__parport_gsc_frob_control (p, 0x20, 0x20);
|
||||
}
|
||||
|
||||
static inline void parport_gsc_data_forward(struct parport *p)
|
||||
{
|
||||
__parport_gsc_frob_control (p, 0x20, 0x00);
|
||||
}
|
||||
|
||||
static inline void parport_gsc_write_control(struct parport *p,
|
||||
unsigned char d)
|
||||
{
|
||||
const unsigned char wm = (PARPORT_CONTROL_STROBE |
|
||||
PARPORT_CONTROL_AUTOFD |
|
||||
PARPORT_CONTROL_INIT |
|
||||
PARPORT_CONTROL_SELECT);
|
||||
|
||||
/* Take this out when drivers have adapted to newer interface. */
|
||||
if (d & 0x20) {
|
||||
printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
|
||||
p->name, p->cad->name);
|
||||
parport_gsc_data_reverse (p);
|
||||
}
|
||||
|
||||
__parport_gsc_frob_control (p, wm, d & wm);
|
||||
}
|
||||
|
||||
static inline unsigned char parport_gsc_read_control(struct parport *p)
|
||||
{
|
||||
const unsigned char rm = (PARPORT_CONTROL_STROBE |
|
||||
PARPORT_CONTROL_AUTOFD |
|
||||
PARPORT_CONTROL_INIT |
|
||||
PARPORT_CONTROL_SELECT);
|
||||
const struct parport_gsc_private *priv = p->physport->private_data;
|
||||
return priv->ctr & rm; /* Use soft copy */
|
||||
}
|
||||
|
||||
static inline unsigned char parport_gsc_frob_control(struct parport *p,
|
||||
unsigned char mask,
|
||||
unsigned char val)
|
||||
{
|
||||
const unsigned char wm = (PARPORT_CONTROL_STROBE |
|
||||
PARPORT_CONTROL_AUTOFD |
|
||||
PARPORT_CONTROL_INIT |
|
||||
PARPORT_CONTROL_SELECT);
|
||||
|
||||
/* Take this out when drivers have adapted to newer interface. */
|
||||
if (mask & 0x20) {
|
||||
printk (KERN_DEBUG "%s (%s): use data_%s for this!\n",
|
||||
p->name, p->cad->name,
|
||||
(val & 0x20) ? "reverse" : "forward");
|
||||
if (val & 0x20)
|
||||
parport_gsc_data_reverse (p);
|
||||
else
|
||||
parport_gsc_data_forward (p);
|
||||
}
|
||||
|
||||
/* Restrict mask and val to control lines. */
|
||||
mask &= wm;
|
||||
val &= wm;
|
||||
|
||||
return __parport_gsc_frob_control (p, mask, val);
|
||||
}
|
||||
|
||||
static inline unsigned char parport_gsc_read_status(struct parport *p)
|
||||
{
|
||||
return parport_readb (STATUS(p));
|
||||
}
|
||||
|
||||
static inline void parport_gsc_disable_irq(struct parport *p)
|
||||
{
|
||||
__parport_gsc_frob_control (p, 0x10, 0x00);
|
||||
}
|
||||
|
||||
static inline void parport_gsc_enable_irq(struct parport *p)
|
||||
{
|
||||
__parport_gsc_frob_control (p, 0x10, 0x10);
|
||||
}
|
||||
|
||||
extern void parport_gsc_release_resources(struct parport *p);
|
||||
|
||||
extern int parport_gsc_claim_resources(struct parport *p);
|
||||
|
||||
extern void parport_gsc_init_state(struct pardevice *, struct parport_state *s);
|
||||
|
||||
extern void parport_gsc_save_state(struct parport *p, struct parport_state *s);
|
||||
|
||||
extern void parport_gsc_restore_state(struct parport *p, struct parport_state *s);
|
||||
|
||||
extern void parport_gsc_inc_use_count(void);
|
||||
|
||||
extern void parport_gsc_dec_use_count(void);
|
||||
|
||||
extern struct parport *parport_gsc_probe_port(unsigned long base,
|
||||
unsigned long base_hi,
|
||||
int irq, int dma,
|
||||
struct parisc_device *padev);
|
||||
|
||||
#endif /* __DRIVERS_PARPORT_PARPORT_GSC_H */
|
2250
drivers/parport/parport_ip32.c
Normal file
2250
drivers/parport/parport_ip32.c
Normal file
File diff suppressed because it is too large
Load diff
370
drivers/parport/parport_mfc3.c
Normal file
370
drivers/parport/parport_mfc3.c
Normal file
|
@ -0,0 +1,370 @@
|
|||
/* Low-level parallel port routines for the Multiface 3 card
|
||||
*
|
||||
* Author: Joerg Dorchain <joerg@dorchain.net>
|
||||
*
|
||||
* (C) The elitist m68k Users(TM)
|
||||
*
|
||||
* based on the existing parport_amiga and lp_mfc
|
||||
*
|
||||
*
|
||||
* From the MFC3 documentation:
|
||||
*
|
||||
* Miscellaneous PIA Details
|
||||
* -------------------------
|
||||
*
|
||||
* The two open-drain interrupt outputs /IRQA and /IRQB are routed to
|
||||
* /INT2 of the Z2 bus.
|
||||
*
|
||||
* The CPU data bus of the PIA (D0-D7) is connected to D8-D15 on the Z2
|
||||
* bus. This means that any PIA registers are accessed at even addresses.
|
||||
*
|
||||
* Centronics Pin Connections for the PIA
|
||||
* --------------------------------------
|
||||
*
|
||||
* The following table shows the connections between the PIA and the
|
||||
* Centronics interface connector. These connections implement a single, but
|
||||
* very complete, Centronics type interface. The Pin column gives the pin
|
||||
* numbers of the PIA. The Centronics pin numbers can be found in the section
|
||||
* "Parallel Connectors".
|
||||
*
|
||||
*
|
||||
* Pin | PIA | Dir | Centronics Names
|
||||
* -------+-----+-----+---------------------------------------------------------
|
||||
* 19 | CB2 | --> | /STROBE (aka /DRDY)
|
||||
* 10-17 | PBx | <-> | DATA0 - DATA7
|
||||
* 18 | CB1 | <-- | /ACK
|
||||
* 40 | CA1 | <-- | BUSY
|
||||
* 3 | PA1 | <-- | PAPER-OUT (aka POUT)
|
||||
* 4 | PA2 | <-- | SELECTED (aka SEL)
|
||||
* 9 | PA7 | --> | /INIT (aka /RESET or /INPUT-PRIME)
|
||||
* 6 | PA4 | <-- | /ERROR (aka /FAULT)
|
||||
* 7 | PA5 | --> | DIR (aka /SELECT-IN)
|
||||
* 8 | PA6 | --> | /AUTO-FEED-XT
|
||||
* 39 | CA2 | --> | open
|
||||
* 5 | PA3 | <-- | /ACK (same as CB1!)
|
||||
* 2 | PA0 | <-- | BUSY (same as CA1!)
|
||||
* -------+-----+-----+---------------------------------------------------------
|
||||
*
|
||||
* Should be enough to understand some of the driver.
|
||||
*
|
||||
* Per convention for normal use the port registers are visible.
|
||||
* If you need the data direction registers, restore the value in the
|
||||
* control register.
|
||||
*/
|
||||
|
||||
#include "multiface.h"
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mc6821.h>
|
||||
#include <linux/zorro.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/amigahw.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/amigaints.h>
|
||||
|
||||
/* Maximum Number of Cards supported */
|
||||
#define MAX_MFC 5
|
||||
|
||||
#undef DEBUG
|
||||
#ifdef DEBUG
|
||||
#define DPRINTK printk
|
||||
#else
|
||||
static inline int DPRINTK(void *nothing, ...) {return 0;}
|
||||
#endif
|
||||
|
||||
static struct parport *this_port[MAX_MFC] = {NULL, };
|
||||
static volatile int dummy; /* for trigger readds */
|
||||
|
||||
#define pia(dev) ((struct pia *)(dev->base))
|
||||
static struct parport_operations pp_mfc3_ops;
|
||||
|
||||
static void mfc3_write_data(struct parport *p, unsigned char data)
|
||||
{
|
||||
DPRINTK(KERN_DEBUG "write_data %c\n",data);
|
||||
|
||||
dummy = pia(p)->pprb; /* clears irq bit */
|
||||
/* Triggers also /STROBE.*/
|
||||
pia(p)->pprb = data;
|
||||
}
|
||||
|
||||
static unsigned char mfc3_read_data(struct parport *p)
|
||||
{
|
||||
/* clears interrupt bit. Triggers also /STROBE. */
|
||||
return pia(p)->pprb;
|
||||
}
|
||||
|
||||
static unsigned char control_pc_to_mfc3(unsigned char control)
|
||||
{
|
||||
unsigned char ret = 32|64;
|
||||
|
||||
if (control & PARPORT_CONTROL_SELECT) /* XXX: What is SELECP? */
|
||||
ret &= ~32; /* /SELECT_IN */
|
||||
if (control & PARPORT_CONTROL_INIT) /* INITP */
|
||||
ret |= 128;
|
||||
if (control & PARPORT_CONTROL_AUTOFD) /* AUTOLF */
|
||||
ret &= ~64;
|
||||
if (control & PARPORT_CONTROL_STROBE) /* Strobe */
|
||||
/* Handled directly by hardware */;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned char control_mfc3_to_pc(unsigned char control)
|
||||
{
|
||||
unsigned char ret = PARPORT_CONTROL_STROBE
|
||||
| PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_SELECT;
|
||||
|
||||
if (control & 128) /* /INITP */
|
||||
ret |= PARPORT_CONTROL_INIT;
|
||||
if (control & 64) /* /AUTOLF */
|
||||
ret &= ~PARPORT_CONTROL_AUTOFD;
|
||||
if (control & 32) /* /SELECT_IN */
|
||||
ret &= ~PARPORT_CONTROL_SELECT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mfc3_write_control(struct parport *p, unsigned char control)
|
||||
{
|
||||
DPRINTK(KERN_DEBUG "write_control %02x\n",control);
|
||||
pia(p)->ppra = (pia(p)->ppra & 0x1f) | control_pc_to_mfc3(control);
|
||||
}
|
||||
|
||||
static unsigned char mfc3_read_control( struct parport *p)
|
||||
{
|
||||
DPRINTK(KERN_DEBUG "read_control \n");
|
||||
return control_mfc3_to_pc(pia(p)->ppra & 0xe0);
|
||||
}
|
||||
|
||||
static unsigned char mfc3_frob_control( struct parport *p, unsigned char mask, unsigned char val)
|
||||
{
|
||||
unsigned char old;
|
||||
|
||||
DPRINTK(KERN_DEBUG "frob_control mask %02x, value %02x\n",mask,val);
|
||||
old = mfc3_read_control(p);
|
||||
mfc3_write_control(p, (old & ~mask) ^ val);
|
||||
return old;
|
||||
}
|
||||
|
||||
static unsigned char status_mfc3_to_pc(unsigned char status)
|
||||
{
|
||||
unsigned char ret = PARPORT_STATUS_BUSY;
|
||||
|
||||
if (status & 1) /* Busy */
|
||||
ret &= ~PARPORT_STATUS_BUSY;
|
||||
if (status & 2) /* PaperOut */
|
||||
ret |= PARPORT_STATUS_PAPEROUT;
|
||||
if (status & 4) /* Selected */
|
||||
ret |= PARPORT_STATUS_SELECT;
|
||||
if (status & 8) /* Ack */
|
||||
ret |= PARPORT_STATUS_ACK;
|
||||
if (status & 16) /* /ERROR */
|
||||
ret |= PARPORT_STATUS_ERROR;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned char mfc3_read_status(struct parport *p)
|
||||
{
|
||||
unsigned char status;
|
||||
|
||||
status = status_mfc3_to_pc(pia(p)->ppra & 0x1f);
|
||||
DPRINTK(KERN_DEBUG "read_status %02x\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int use_cnt = 0;
|
||||
|
||||
static irqreturn_t mfc3_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < MAX_MFC; i++)
|
||||
if (this_port[i] != NULL)
|
||||
if (pia(this_port[i])->crb & 128) { /* Board caused interrupt */
|
||||
dummy = pia(this_port[i])->pprb; /* clear irq bit */
|
||||
parport_generic_irq(this_port[i]);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void mfc3_enable_irq(struct parport *p)
|
||||
{
|
||||
pia(p)->crb |= PIA_C1_ENABLE_IRQ;
|
||||
}
|
||||
|
||||
static void mfc3_disable_irq(struct parport *p)
|
||||
{
|
||||
pia(p)->crb &= ~PIA_C1_ENABLE_IRQ;
|
||||
}
|
||||
|
||||
static void mfc3_data_forward(struct parport *p)
|
||||
{
|
||||
DPRINTK(KERN_DEBUG "forward\n");
|
||||
pia(p)->crb &= ~PIA_DDR; /* make data direction register visible */
|
||||
pia(p)->pddrb = 255; /* all pins output */
|
||||
pia(p)->crb |= PIA_DDR; /* make data register visible - default */
|
||||
}
|
||||
|
||||
static void mfc3_data_reverse(struct parport *p)
|
||||
{
|
||||
DPRINTK(KERN_DEBUG "reverse\n");
|
||||
pia(p)->crb &= ~PIA_DDR; /* make data direction register visible */
|
||||
pia(p)->pddrb = 0; /* all pins input */
|
||||
pia(p)->crb |= PIA_DDR; /* make data register visible - default */
|
||||
}
|
||||
|
||||
static void mfc3_init_state(struct pardevice *dev, struct parport_state *s)
|
||||
{
|
||||
s->u.amiga.data = 0;
|
||||
s->u.amiga.datadir = 255;
|
||||
s->u.amiga.status = 0;
|
||||
s->u.amiga.statusdir = 0xe0;
|
||||
}
|
||||
|
||||
static void mfc3_save_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
s->u.amiga.data = pia(p)->pprb;
|
||||
pia(p)->crb &= ~PIA_DDR;
|
||||
s->u.amiga.datadir = pia(p)->pddrb;
|
||||
pia(p)->crb |= PIA_DDR;
|
||||
s->u.amiga.status = pia(p)->ppra;
|
||||
pia(p)->cra &= ~PIA_DDR;
|
||||
s->u.amiga.statusdir = pia(p)->pddrb;
|
||||
pia(p)->cra |= PIA_DDR;
|
||||
}
|
||||
|
||||
static void mfc3_restore_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
pia(p)->pprb = s->u.amiga.data;
|
||||
pia(p)->crb &= ~PIA_DDR;
|
||||
pia(p)->pddrb = s->u.amiga.datadir;
|
||||
pia(p)->crb |= PIA_DDR;
|
||||
pia(p)->ppra = s->u.amiga.status;
|
||||
pia(p)->cra &= ~PIA_DDR;
|
||||
pia(p)->pddrb = s->u.amiga.statusdir;
|
||||
pia(p)->cra |= PIA_DDR;
|
||||
}
|
||||
|
||||
static struct parport_operations pp_mfc3_ops = {
|
||||
.write_data = mfc3_write_data,
|
||||
.read_data = mfc3_read_data,
|
||||
|
||||
.write_control = mfc3_write_control,
|
||||
.read_control = mfc3_read_control,
|
||||
.frob_control = mfc3_frob_control,
|
||||
|
||||
.read_status = mfc3_read_status,
|
||||
|
||||
.enable_irq = mfc3_enable_irq,
|
||||
.disable_irq = mfc3_disable_irq,
|
||||
|
||||
.data_forward = mfc3_data_forward,
|
||||
.data_reverse = mfc3_data_reverse,
|
||||
|
||||
.init_state = mfc3_init_state,
|
||||
.save_state = mfc3_save_state,
|
||||
.restore_state = mfc3_restore_state,
|
||||
|
||||
.epp_write_data = parport_ieee1284_epp_write_data,
|
||||
.epp_read_data = parport_ieee1284_epp_read_data,
|
||||
.epp_write_addr = parport_ieee1284_epp_write_addr,
|
||||
.epp_read_addr = parport_ieee1284_epp_read_addr,
|
||||
|
||||
.ecp_write_data = parport_ieee1284_ecp_write_data,
|
||||
.ecp_read_data = parport_ieee1284_ecp_read_data,
|
||||
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
|
||||
|
||||
.compat_write_data = parport_ieee1284_write_compat,
|
||||
.nibble_read_data = parport_ieee1284_read_nibble,
|
||||
.byte_read_data = parport_ieee1284_read_byte,
|
||||
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/* ----------- Initialisation code --------------------------------- */
|
||||
|
||||
static int __init parport_mfc3_init(void)
|
||||
{
|
||||
struct parport *p;
|
||||
int pias = 0;
|
||||
struct pia *pp;
|
||||
struct zorro_dev *z = NULL;
|
||||
|
||||
if (!MACH_IS_AMIGA)
|
||||
return -ENODEV;
|
||||
|
||||
while ((z = zorro_find_device(ZORRO_PROD_BSC_MULTIFACE_III, z))) {
|
||||
unsigned long piabase = z->resource.start+PIABASE;
|
||||
if (!request_mem_region(piabase, sizeof(struct pia), "PIA"))
|
||||
continue;
|
||||
|
||||
pp = ZTWO_VADDR(piabase);
|
||||
pp->crb = 0;
|
||||
pp->pddrb = 255; /* all data pins output */
|
||||
pp->crb = PIA_DDR|32|8;
|
||||
dummy = pp->pddrb; /* reading clears interrupt */
|
||||
pp->cra = 0;
|
||||
pp->pddra = 0xe0; /* /RESET, /DIR ,/AUTO-FEED output */
|
||||
pp->cra = PIA_DDR;
|
||||
pp->ppra = 0; /* reset printer */
|
||||
udelay(10);
|
||||
pp->ppra = 128;
|
||||
p = parport_register_port((unsigned long)pp, IRQ_AMIGA_PORTS,
|
||||
PARPORT_DMA_NONE, &pp_mfc3_ops);
|
||||
if (!p)
|
||||
goto out_port;
|
||||
|
||||
if (p->irq != PARPORT_IRQ_NONE) {
|
||||
if (use_cnt++ == 0)
|
||||
if (request_irq(IRQ_AMIGA_PORTS, mfc3_interrupt, IRQF_SHARED, p->name, &pp_mfc3_ops))
|
||||
goto out_irq;
|
||||
}
|
||||
p->dev = &z->dev;
|
||||
|
||||
this_port[pias++] = p;
|
||||
printk(KERN_INFO "%s: Multiface III port using irq\n", p->name);
|
||||
/* XXX: set operating mode */
|
||||
|
||||
p->private_data = (void *)piabase;
|
||||
parport_announce_port (p);
|
||||
|
||||
if (pias >= MAX_MFC)
|
||||
break;
|
||||
continue;
|
||||
|
||||
out_irq:
|
||||
parport_put_port(p);
|
||||
out_port:
|
||||
release_mem_region(piabase, sizeof(struct pia));
|
||||
}
|
||||
|
||||
return pias ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static void __exit parport_mfc3_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_MFC; i++) {
|
||||
if (!this_port[i])
|
||||
continue;
|
||||
parport_remove_port(this_port[i]);
|
||||
if (this_port[i]->irq != PARPORT_IRQ_NONE) {
|
||||
if (--use_cnt == 0)
|
||||
free_irq(IRQ_AMIGA_PORTS, &pp_mfc3_ops);
|
||||
}
|
||||
release_mem_region(ZTWO_PADDR(this_port[i]->private_data), sizeof(struct pia));
|
||||
parport_put_port(this_port[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>");
|
||||
MODULE_DESCRIPTION("Parport Driver for Multiface 3 expansion cards Parallel Port");
|
||||
MODULE_SUPPORTED_DEVICE("Multiface 3 Parallel Port");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(parport_mfc3_init)
|
||||
module_exit(parport_mfc3_exit)
|
3357
drivers/parport/parport_pc.c
Normal file
3357
drivers/parport/parport_pc.c
Normal file
File diff suppressed because it is too large
Load diff
715
drivers/parport/parport_serial.c
Normal file
715
drivers/parport/parport_serial.c
Normal file
|
@ -0,0 +1,715 @@
|
|||
/*
|
||||
* Support for common PCI multi-I/O cards (which is most of them)
|
||||
*
|
||||
* Copyright (C) 2001 Tim Waugh <twaugh@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* Multi-function PCI cards are supposed to present separate logical
|
||||
* devices on the bus. A common thing to do seems to be to just use
|
||||
* one logical device with lots of base address registers for both
|
||||
* parallel ports and serial ports. This driver is for dealing with
|
||||
* that.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/parport_pc.h>
|
||||
#include <linux/8250_pci.h>
|
||||
|
||||
enum parport_pc_pci_cards {
|
||||
titan_110l = 0,
|
||||
titan_210l,
|
||||
netmos_9xx5_combo,
|
||||
netmos_9855,
|
||||
netmos_9855_2p,
|
||||
netmos_9900,
|
||||
netmos_9900_2p,
|
||||
netmos_99xx_1p,
|
||||
avlab_1s1p,
|
||||
avlab_1s2p,
|
||||
avlab_2s1p,
|
||||
siig_1s1p_10x,
|
||||
siig_2s1p_10x,
|
||||
siig_2p1s_20x,
|
||||
siig_1s1p_20x,
|
||||
siig_2s1p_20x,
|
||||
timedia_4078a,
|
||||
timedia_4079h,
|
||||
timedia_4085h,
|
||||
timedia_4088a,
|
||||
timedia_4089a,
|
||||
timedia_4095a,
|
||||
timedia_4096a,
|
||||
timedia_4078u,
|
||||
timedia_4079a,
|
||||
timedia_4085u,
|
||||
timedia_4079r,
|
||||
timedia_4079s,
|
||||
timedia_4079d,
|
||||
timedia_4079e,
|
||||
timedia_4079f,
|
||||
timedia_9079a,
|
||||
timedia_9079b,
|
||||
timedia_9079c,
|
||||
wch_ch353_1s1p,
|
||||
wch_ch353_2s1p,
|
||||
sunix_2s1p,
|
||||
};
|
||||
|
||||
/* each element directly indexed from enum list, above */
|
||||
struct parport_pc_pci {
|
||||
int numports;
|
||||
struct { /* BAR (base address registers) numbers in the config
|
||||
space header */
|
||||
int lo;
|
||||
int hi; /* -1 if not there, >6 for offset-method (max
|
||||
BAR is 6) */
|
||||
} addr[4];
|
||||
|
||||
/* If set, this is called immediately after pci_enable_device.
|
||||
* If it returns non-zero, no probing will take place and the
|
||||
* ports will not be used. */
|
||||
int (*preinit_hook) (struct pci_dev *pdev, struct parport_pc_pci *card,
|
||||
int autoirq, int autodma);
|
||||
|
||||
/* If set, this is called after probing for ports. If 'failed'
|
||||
* is non-zero we couldn't use any of the ports. */
|
||||
void (*postinit_hook) (struct pci_dev *pdev,
|
||||
struct parport_pc_pci *card, int failed);
|
||||
};
|
||||
|
||||
static int netmos_parallel_init(struct pci_dev *dev, struct parport_pc_pci *par,
|
||||
int autoirq, int autodma)
|
||||
{
|
||||
/* the rule described below doesn't hold for this device */
|
||||
if (dev->device == PCI_DEVICE_ID_NETMOS_9835 &&
|
||||
dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
|
||||
dev->subsystem_device == 0x0299)
|
||||
return -ENODEV;
|
||||
|
||||
if (dev->device == PCI_DEVICE_ID_NETMOS_9912) {
|
||||
par->numports = 1;
|
||||
} else {
|
||||
/*
|
||||
* Netmos uses the subdevice ID to indicate the number of parallel
|
||||
* and serial ports. The form is 0x00PS, where <P> is the number of
|
||||
* parallel ports and <S> is the number of serial ports.
|
||||
*/
|
||||
par->numports = (dev->subsystem_device & 0xf0) >> 4;
|
||||
if (par->numports > ARRAY_SIZE(par->addr))
|
||||
par->numports = ARRAY_SIZE(par->addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct parport_pc_pci cards[] = {
|
||||
/* titan_110l */ { 1, { { 3, -1 }, } },
|
||||
/* titan_210l */ { 1, { { 3, -1 }, } },
|
||||
/* netmos_9xx5_combo */ { 1, { { 2, -1 }, }, netmos_parallel_init },
|
||||
/* netmos_9855 */ { 1, { { 0, -1 }, }, netmos_parallel_init },
|
||||
/* netmos_9855_2p */ { 2, { { 0, -1 }, { 2, -1 }, } },
|
||||
/* netmos_9900 */ {1, { { 3, 4 }, }, netmos_parallel_init },
|
||||
/* netmos_9900_2p */ {2, { { 0, 1 }, { 3, 4 }, } },
|
||||
/* netmos_99xx_1p */ {1, { { 0, 1 }, } },
|
||||
/* avlab_1s1p */ { 1, { { 1, 2}, } },
|
||||
/* avlab_1s2p */ { 2, { { 1, 2}, { 3, 4 },} },
|
||||
/* avlab_2s1p */ { 1, { { 2, 3}, } },
|
||||
/* siig_1s1p_10x */ { 1, { { 3, 4 }, } },
|
||||
/* siig_2s1p_10x */ { 1, { { 4, 5 }, } },
|
||||
/* siig_2p1s_20x */ { 2, { { 1, 2 }, { 3, 4 }, } },
|
||||
/* siig_1s1p_20x */ { 1, { { 1, 2 }, } },
|
||||
/* siig_2s1p_20x */ { 1, { { 2, 3 }, } },
|
||||
/* timedia_4078a */ { 1, { { 2, -1 }, } },
|
||||
/* timedia_4079h */ { 1, { { 2, 3 }, } },
|
||||
/* timedia_4085h */ { 2, { { 2, -1 }, { 4, -1 }, } },
|
||||
/* timedia_4088a */ { 2, { { 2, 3 }, { 4, 5 }, } },
|
||||
/* timedia_4089a */ { 2, { { 2, 3 }, { 4, 5 }, } },
|
||||
/* timedia_4095a */ { 2, { { 2, 3 }, { 4, 5 }, } },
|
||||
/* timedia_4096a */ { 2, { { 2, 3 }, { 4, 5 }, } },
|
||||
/* timedia_4078u */ { 1, { { 2, -1 }, } },
|
||||
/* timedia_4079a */ { 1, { { 2, 3 }, } },
|
||||
/* timedia_4085u */ { 2, { { 2, -1 }, { 4, -1 }, } },
|
||||
/* timedia_4079r */ { 1, { { 2, 3 }, } },
|
||||
/* timedia_4079s */ { 1, { { 2, 3 }, } },
|
||||
/* timedia_4079d */ { 1, { { 2, 3 }, } },
|
||||
/* timedia_4079e */ { 1, { { 2, 3 }, } },
|
||||
/* timedia_4079f */ { 1, { { 2, 3 }, } },
|
||||
/* timedia_9079a */ { 1, { { 2, 3 }, } },
|
||||
/* timedia_9079b */ { 1, { { 2, 3 }, } },
|
||||
/* timedia_9079c */ { 1, { { 2, 3 }, } },
|
||||
/* wch_ch353_1s1p*/ { 1, { { 1, -1}, } },
|
||||
/* wch_ch353_2s1p*/ { 1, { { 2, -1}, } },
|
||||
/* sunix_2s1p */ { 1, { { 3, -1 }, } },
|
||||
};
|
||||
|
||||
#define PCI_VENDOR_ID_SUNIX 0x1fd4
|
||||
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
|
||||
|
||||
static struct pci_device_id parport_serial_pci_tbl[] = {
|
||||
/* PCI cards */
|
||||
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l },
|
||||
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9745,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
|
||||
0x1000, 0x0020, 0, 0, netmos_9855_2p },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
|
||||
0x1000, 0x0022, 0, 0, netmos_9855_2p },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
|
||||
0xA000, 0x3011, 0, 0, netmos_9900 },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
|
||||
0xA000, 0x3012, 0, 0, netmos_9900 },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
|
||||
0xA000, 0x3020, 0, 0, netmos_9900_2p },
|
||||
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912,
|
||||
0xA000, 0x2000, 0, 0, netmos_99xx_1p },
|
||||
/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
|
||||
{ PCI_VENDOR_ID_AFAVLAB, 0x2110,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
|
||||
{ PCI_VENDOR_ID_AFAVLAB, 0x2111,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
|
||||
{ PCI_VENDOR_ID_AFAVLAB, 0x2112,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
|
||||
{ PCI_VENDOR_ID_AFAVLAB, 0x2140,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
|
||||
{ PCI_VENDOR_ID_AFAVLAB, 0x2141,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
|
||||
{ PCI_VENDOR_ID_AFAVLAB, 0x2142,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
|
||||
{ PCI_VENDOR_ID_AFAVLAB, 0x2160,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
|
||||
{ PCI_VENDOR_ID_AFAVLAB, 0x2161,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
|
||||
{ PCI_VENDOR_ID_AFAVLAB, 0x2162,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
|
||||
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
|
||||
/* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/
|
||||
{ 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d },
|
||||
{ 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e },
|
||||
{ 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f },
|
||||
{ 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a },
|
||||
{ 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
|
||||
{ 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
|
||||
|
||||
/* WCH CARDS */
|
||||
{ 0x4348, 0x5053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p},
|
||||
{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
|
||||
|
||||
/*
|
||||
* More SUNIX variations. At least one of these has part number
|
||||
* '5079A but subdevice 0x102. That board reports 0x0708 as
|
||||
* its PCI Class.
|
||||
*/
|
||||
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
|
||||
0x0102, 0, 0, sunix_2s1p },
|
||||
|
||||
{ 0, } /* terminate list */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
|
||||
|
||||
/*
|
||||
* This table describes the serial "geometry" of these boards. Any
|
||||
* quirks for these can be found in drivers/serial/8250_pci.c
|
||||
*
|
||||
* Cards not tested are marked n/t
|
||||
* If you have one of these cards and it works for you, please tell me..
|
||||
*/
|
||||
static struct pciserial_board pci_parport_serial_boards[] = {
|
||||
[titan_110l] = {
|
||||
.flags = FL_BASE1 | FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[titan_210l] = {
|
||||
.flags = FL_BASE1 | FL_BASE_BARS,
|
||||
.num_ports = 2,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[netmos_9xx5_combo] = {
|
||||
.flags = FL_BASE0 | FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[netmos_9855] = {
|
||||
.flags = FL_BASE2 | FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[netmos_9855_2p] = {
|
||||
.flags = FL_BASE4 | FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[netmos_9900] = { /* n/t */
|
||||
.flags = FL_BASE0 | FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[netmos_9900_2p] = { /* parallel only */ /* n/t */
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 0,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[netmos_99xx_1p] = { /* parallel only */ /* n/t */
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 0,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[avlab_1s1p] = { /* n/t */
|
||||
.flags = FL_BASE0 | FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[avlab_1s2p] = { /* n/t */
|
||||
.flags = FL_BASE0 | FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[avlab_2s1p] = { /* n/t */
|
||||
.flags = FL_BASE0 | FL_BASE_BARS,
|
||||
.num_ports = 2,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[siig_1s1p_10x] = {
|
||||
.flags = FL_BASE2,
|
||||
.num_ports = 1,
|
||||
.base_baud = 460800,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[siig_2s1p_10x] = {
|
||||
.flags = FL_BASE2,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[siig_2p1s_20x] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[siig_1s1p_20x] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[siig_2s1p_20x] = {
|
||||
.flags = FL_BASE0,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4078a] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4079h] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4085h] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4088a] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4089a] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4095a] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4096a] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4078u] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4079a] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4085u] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4079r] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4079s] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4079d] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4079e] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_4079f] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_9079a] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_9079b] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[timedia_9079c] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[wch_ch353_1s1p] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 1,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[wch_ch353_2s1p] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 2,
|
||||
.base_baud = 115200,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
[sunix_2s1p] = {
|
||||
.flags = FL_BASE0|FL_BASE_BARS,
|
||||
.num_ports = 2,
|
||||
.base_baud = 921600,
|
||||
.uart_offset = 8,
|
||||
},
|
||||
};
|
||||
|
||||
struct parport_serial_private {
|
||||
struct serial_private *serial;
|
||||
int num_par;
|
||||
struct parport *port[PARPORT_MAX];
|
||||
struct parport_pc_pci par;
|
||||
};
|
||||
|
||||
/* Register the serial port(s) of a PCI card. */
|
||||
static int serial_register(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct parport_serial_private *priv = pci_get_drvdata (dev);
|
||||
struct pciserial_board *board;
|
||||
struct serial_private *serial;
|
||||
|
||||
board = &pci_parport_serial_boards[id->driver_data];
|
||||
|
||||
if (board->num_ports == 0)
|
||||
return 0;
|
||||
|
||||
serial = pciserial_init_ports(dev, board);
|
||||
|
||||
if (IS_ERR(serial))
|
||||
return PTR_ERR(serial);
|
||||
|
||||
priv->serial = serial;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register the parallel port(s) of a PCI card. */
|
||||
static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct parport_pc_pci *card;
|
||||
struct parport_serial_private *priv = pci_get_drvdata (dev);
|
||||
int n, success = 0;
|
||||
|
||||
priv->par = cards[id->driver_data];
|
||||
card = &priv->par;
|
||||
if (card->preinit_hook &&
|
||||
card->preinit_hook (dev, card, PARPORT_IRQ_NONE, PARPORT_DMA_NONE))
|
||||
return -ENODEV;
|
||||
|
||||
for (n = 0; n < card->numports; n++) {
|
||||
struct parport *port;
|
||||
int lo = card->addr[n].lo;
|
||||
int hi = card->addr[n].hi;
|
||||
unsigned long io_lo, io_hi;
|
||||
int irq;
|
||||
|
||||
if (priv->num_par == ARRAY_SIZE (priv->port)) {
|
||||
printk (KERN_WARNING
|
||||
"parport_serial: %s: only %zu parallel ports "
|
||||
"supported (%d reported)\n", pci_name (dev),
|
||||
ARRAY_SIZE(priv->port), card->numports);
|
||||
break;
|
||||
}
|
||||
|
||||
io_lo = pci_resource_start (dev, lo);
|
||||
io_hi = 0;
|
||||
if ((hi >= 0) && (hi <= 6))
|
||||
io_hi = pci_resource_start (dev, hi);
|
||||
else if (hi > 6)
|
||||
io_lo += hi; /* Reinterpret the meaning of
|
||||
"hi" as an offset (see SYBA
|
||||
def.) */
|
||||
/* TODO: test if sharing interrupts works */
|
||||
irq = dev->irq;
|
||||
if (irq == IRQ_NONE) {
|
||||
dev_dbg(&dev->dev,
|
||||
"PCI parallel port detected: I/O at %#lx(%#lx)\n",
|
||||
io_lo, io_hi);
|
||||
irq = PARPORT_IRQ_NONE;
|
||||
} else {
|
||||
dev_dbg(&dev->dev,
|
||||
"PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
|
||||
io_lo, io_hi, irq);
|
||||
}
|
||||
port = parport_pc_probe_port (io_lo, io_hi, irq,
|
||||
PARPORT_DMA_NONE, &dev->dev, IRQF_SHARED);
|
||||
if (port) {
|
||||
priv->port[priv->num_par++] = port;
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (card->postinit_hook)
|
||||
card->postinit_hook (dev, card, !success);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parport_serial_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct parport_serial_private *priv;
|
||||
int err;
|
||||
|
||||
priv = kzalloc (sizeof *priv, GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
pci_set_drvdata (dev, priv);
|
||||
|
||||
err = pci_enable_device (dev);
|
||||
if (err) {
|
||||
kfree (priv);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (parport_register (dev, id)) {
|
||||
kfree (priv);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (serial_register (dev, id)) {
|
||||
int i;
|
||||
for (i = 0; i < priv->num_par; i++)
|
||||
parport_pc_unregister_port (priv->port[i]);
|
||||
kfree (priv);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parport_serial_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct parport_serial_private *priv = pci_get_drvdata (dev);
|
||||
int i;
|
||||
|
||||
// Serial ports
|
||||
if (priv->serial)
|
||||
pciserial_remove_ports(priv->serial);
|
||||
|
||||
// Parallel ports
|
||||
for (i = 0; i < priv->num_par; i++)
|
||||
parport_pc_unregister_port (priv->port[i]);
|
||||
|
||||
kfree (priv);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state)
|
||||
{
|
||||
struct parport_serial_private *priv = pci_get_drvdata(dev);
|
||||
|
||||
if (priv->serial)
|
||||
pciserial_suspend_ports(priv->serial);
|
||||
|
||||
/* FIXME: What about parport? */
|
||||
|
||||
pci_save_state(dev);
|
||||
pci_set_power_state(dev, pci_choose_state(dev, state));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parport_serial_pci_resume(struct pci_dev *dev)
|
||||
{
|
||||
struct parport_serial_private *priv = pci_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
pci_set_power_state(dev, PCI_D0);
|
||||
pci_restore_state(dev);
|
||||
|
||||
/*
|
||||
* The device may have been disabled. Re-enable it.
|
||||
*/
|
||||
err = pci_enable_device(dev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "parport_serial: %s: error enabling "
|
||||
"device for resume (%d)\n", pci_name(dev), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (priv->serial)
|
||||
pciserial_resume_ports(priv->serial);
|
||||
|
||||
/* FIXME: What about parport? */
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct pci_driver parport_serial_pci_driver = {
|
||||
.name = "parport_serial",
|
||||
.id_table = parport_serial_pci_tbl,
|
||||
.probe = parport_serial_pci_probe,
|
||||
.remove = parport_serial_pci_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = parport_serial_pci_suspend,
|
||||
.resume = parport_serial_pci_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
static int __init parport_serial_init (void)
|
||||
{
|
||||
return pci_register_driver (&parport_serial_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit parport_serial_exit (void)
|
||||
{
|
||||
pci_unregister_driver (&parport_serial_pci_driver);
|
||||
return;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Tim Waugh <twaugh@redhat.com>");
|
||||
MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(parport_serial_init);
|
||||
module_exit(parport_serial_exit);
|
378
drivers/parport/parport_sunbpp.c
Normal file
378
drivers/parport/parport_sunbpp.c
Normal file
|
@ -0,0 +1,378 @@
|
|||
/* parport_sunbpp.c: Parallel-port routines for SBUS
|
||||
*
|
||||
* Author: Derrick J. Brashear <shadow@dementia.org>
|
||||
*
|
||||
* based on work by:
|
||||
* Phil Blundell <philb@gnu.org>
|
||||
* Tim Waugh <tim@cyberelk.demon.co.uk>
|
||||
* Jose Renau <renau@acm.org>
|
||||
* David Campbell <campbell@tirian.che.curtin.edu.au>
|
||||
* Grant Guenther <grant@torque.net>
|
||||
* Eddie C. Dost <ecd@skynet.be>
|
||||
* Stephen Williams (steve@icarus.com)
|
||||
* Gus Baldauf (gbaldauf@ix.netcom.com)
|
||||
* Peter Zaitcev
|
||||
* Tom Dyas
|
||||
*
|
||||
* Updated to new SBUS device framework: David S. Miller <davem@davemloft.net>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <linux/parport.h>
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/oplib.h> /* OpenProm Library */
|
||||
#include <asm/dma.h> /* BPP uses LSI 64854 for DMA */
|
||||
#include <asm/irq.h>
|
||||
#include <asm/sunbpp.h>
|
||||
|
||||
#undef __SUNBPP_DEBUG
|
||||
#ifdef __SUNBPP_DEBUG
|
||||
#define dprintk(x) printk x
|
||||
#else
|
||||
#define dprintk(x)
|
||||
#endif
|
||||
|
||||
static void parport_sunbpp_disable_irq(struct parport *p)
|
||||
{
|
||||
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
|
||||
u32 tmp;
|
||||
|
||||
tmp = sbus_readl(®s->p_csr);
|
||||
tmp &= ~DMA_INT_ENAB;
|
||||
sbus_writel(tmp, ®s->p_csr);
|
||||
}
|
||||
|
||||
static void parport_sunbpp_enable_irq(struct parport *p)
|
||||
{
|
||||
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
|
||||
u32 tmp;
|
||||
|
||||
tmp = sbus_readl(®s->p_csr);
|
||||
tmp |= DMA_INT_ENAB;
|
||||
sbus_writel(tmp, ®s->p_csr);
|
||||
}
|
||||
|
||||
static void parport_sunbpp_write_data(struct parport *p, unsigned char d)
|
||||
{
|
||||
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
|
||||
|
||||
sbus_writeb(d, ®s->p_dr);
|
||||
dprintk((KERN_DEBUG "wrote 0x%x\n", d));
|
||||
}
|
||||
|
||||
static unsigned char parport_sunbpp_read_data(struct parport *p)
|
||||
{
|
||||
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
|
||||
|
||||
return sbus_readb(®s->p_dr);
|
||||
}
|
||||
|
||||
static unsigned char status_sunbpp_to_pc(struct parport *p)
|
||||
{
|
||||
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
|
||||
unsigned char bits = 0;
|
||||
unsigned char value_tcr = sbus_readb(®s->p_tcr);
|
||||
unsigned char value_ir = sbus_readb(®s->p_ir);
|
||||
|
||||
if (!(value_ir & P_IR_ERR))
|
||||
bits |= PARPORT_STATUS_ERROR;
|
||||
if (!(value_ir & P_IR_SLCT))
|
||||
bits |= PARPORT_STATUS_SELECT;
|
||||
if (!(value_ir & P_IR_PE))
|
||||
bits |= PARPORT_STATUS_PAPEROUT;
|
||||
if (value_tcr & P_TCR_ACK)
|
||||
bits |= PARPORT_STATUS_ACK;
|
||||
if (!(value_tcr & P_TCR_BUSY))
|
||||
bits |= PARPORT_STATUS_BUSY;
|
||||
|
||||
dprintk((KERN_DEBUG "tcr 0x%x ir 0x%x\n", value_tcr, value_ir));
|
||||
dprintk((KERN_DEBUG "read status 0x%x\n", bits));
|
||||
return bits;
|
||||
}
|
||||
|
||||
static unsigned char control_sunbpp_to_pc(struct parport *p)
|
||||
{
|
||||
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
|
||||
unsigned char bits = 0;
|
||||
unsigned char value_tcr = sbus_readb(®s->p_tcr);
|
||||
unsigned char value_or = sbus_readb(®s->p_or);
|
||||
|
||||
if (!(value_tcr & P_TCR_DS))
|
||||
bits |= PARPORT_CONTROL_STROBE;
|
||||
if (!(value_or & P_OR_AFXN))
|
||||
bits |= PARPORT_CONTROL_AUTOFD;
|
||||
if (!(value_or & P_OR_INIT))
|
||||
bits |= PARPORT_CONTROL_INIT;
|
||||
if (value_or & P_OR_SLCT_IN)
|
||||
bits |= PARPORT_CONTROL_SELECT;
|
||||
|
||||
dprintk((KERN_DEBUG "tcr 0x%x or 0x%x\n", value_tcr, value_or));
|
||||
dprintk((KERN_DEBUG "read control 0x%x\n", bits));
|
||||
return bits;
|
||||
}
|
||||
|
||||
static unsigned char parport_sunbpp_read_control(struct parport *p)
|
||||
{
|
||||
return control_sunbpp_to_pc(p);
|
||||
}
|
||||
|
||||
static unsigned char parport_sunbpp_frob_control(struct parport *p,
|
||||
unsigned char mask,
|
||||
unsigned char val)
|
||||
{
|
||||
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
|
||||
unsigned char value_tcr = sbus_readb(®s->p_tcr);
|
||||
unsigned char value_or = sbus_readb(®s->p_or);
|
||||
|
||||
dprintk((KERN_DEBUG "frob1: tcr 0x%x or 0x%x\n",
|
||||
value_tcr, value_or));
|
||||
if (mask & PARPORT_CONTROL_STROBE) {
|
||||
if (val & PARPORT_CONTROL_STROBE) {
|
||||
value_tcr &= ~P_TCR_DS;
|
||||
} else {
|
||||
value_tcr |= P_TCR_DS;
|
||||
}
|
||||
}
|
||||
if (mask & PARPORT_CONTROL_AUTOFD) {
|
||||
if (val & PARPORT_CONTROL_AUTOFD) {
|
||||
value_or &= ~P_OR_AFXN;
|
||||
} else {
|
||||
value_or |= P_OR_AFXN;
|
||||
}
|
||||
}
|
||||
if (mask & PARPORT_CONTROL_INIT) {
|
||||
if (val & PARPORT_CONTROL_INIT) {
|
||||
value_or &= ~P_OR_INIT;
|
||||
} else {
|
||||
value_or |= P_OR_INIT;
|
||||
}
|
||||
}
|
||||
if (mask & PARPORT_CONTROL_SELECT) {
|
||||
if (val & PARPORT_CONTROL_SELECT) {
|
||||
value_or |= P_OR_SLCT_IN;
|
||||
} else {
|
||||
value_or &= ~P_OR_SLCT_IN;
|
||||
}
|
||||
}
|
||||
|
||||
sbus_writeb(value_or, ®s->p_or);
|
||||
sbus_writeb(value_tcr, ®s->p_tcr);
|
||||
dprintk((KERN_DEBUG "frob2: tcr 0x%x or 0x%x\n",
|
||||
value_tcr, value_or));
|
||||
return parport_sunbpp_read_control(p);
|
||||
}
|
||||
|
||||
static void parport_sunbpp_write_control(struct parport *p, unsigned char d)
|
||||
{
|
||||
const unsigned char wm = (PARPORT_CONTROL_STROBE |
|
||||
PARPORT_CONTROL_AUTOFD |
|
||||
PARPORT_CONTROL_INIT |
|
||||
PARPORT_CONTROL_SELECT);
|
||||
|
||||
parport_sunbpp_frob_control (p, wm, d & wm);
|
||||
}
|
||||
|
||||
static unsigned char parport_sunbpp_read_status(struct parport *p)
|
||||
{
|
||||
return status_sunbpp_to_pc(p);
|
||||
}
|
||||
|
||||
static void parport_sunbpp_data_forward (struct parport *p)
|
||||
{
|
||||
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
|
||||
unsigned char value_tcr = sbus_readb(®s->p_tcr);
|
||||
|
||||
dprintk((KERN_DEBUG "forward\n"));
|
||||
value_tcr &= ~P_TCR_DIR;
|
||||
sbus_writeb(value_tcr, ®s->p_tcr);
|
||||
}
|
||||
|
||||
static void parport_sunbpp_data_reverse (struct parport *p)
|
||||
{
|
||||
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
|
||||
u8 val = sbus_readb(®s->p_tcr);
|
||||
|
||||
dprintk((KERN_DEBUG "reverse\n"));
|
||||
val |= P_TCR_DIR;
|
||||
sbus_writeb(val, ®s->p_tcr);
|
||||
}
|
||||
|
||||
static void parport_sunbpp_init_state(struct pardevice *dev, struct parport_state *s)
|
||||
{
|
||||
s->u.pc.ctr = 0xc;
|
||||
s->u.pc.ecr = 0x0;
|
||||
}
|
||||
|
||||
static void parport_sunbpp_save_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
s->u.pc.ctr = parport_sunbpp_read_control(p);
|
||||
}
|
||||
|
||||
static void parport_sunbpp_restore_state(struct parport *p, struct parport_state *s)
|
||||
{
|
||||
parport_sunbpp_write_control(p, s->u.pc.ctr);
|
||||
}
|
||||
|
||||
static struct parport_operations parport_sunbpp_ops =
|
||||
{
|
||||
.write_data = parport_sunbpp_write_data,
|
||||
.read_data = parport_sunbpp_read_data,
|
||||
|
||||
.write_control = parport_sunbpp_write_control,
|
||||
.read_control = parport_sunbpp_read_control,
|
||||
.frob_control = parport_sunbpp_frob_control,
|
||||
|
||||
.read_status = parport_sunbpp_read_status,
|
||||
|
||||
.enable_irq = parport_sunbpp_enable_irq,
|
||||
.disable_irq = parport_sunbpp_disable_irq,
|
||||
|
||||
.data_forward = parport_sunbpp_data_forward,
|
||||
.data_reverse = parport_sunbpp_data_reverse,
|
||||
|
||||
.init_state = parport_sunbpp_init_state,
|
||||
.save_state = parport_sunbpp_save_state,
|
||||
.restore_state = parport_sunbpp_restore_state,
|
||||
|
||||
.epp_write_data = parport_ieee1284_epp_write_data,
|
||||
.epp_read_data = parport_ieee1284_epp_read_data,
|
||||
.epp_write_addr = parport_ieee1284_epp_write_addr,
|
||||
.epp_read_addr = parport_ieee1284_epp_read_addr,
|
||||
|
||||
.ecp_write_data = parport_ieee1284_ecp_write_data,
|
||||
.ecp_read_data = parport_ieee1284_ecp_read_data,
|
||||
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
|
||||
|
||||
.compat_write_data = parport_ieee1284_write_compat,
|
||||
.nibble_read_data = parport_ieee1284_read_nibble,
|
||||
.byte_read_data = parport_ieee1284_read_byte,
|
||||
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int bpp_probe(struct platform_device *op)
|
||||
{
|
||||
struct parport_operations *ops;
|
||||
struct bpp_regs __iomem *regs;
|
||||
int irq, dma, err = 0, size;
|
||||
unsigned char value_tcr;
|
||||
void __iomem *base;
|
||||
struct parport *p;
|
||||
|
||||
irq = op->archdata.irqs[0];
|
||||
base = of_ioremap(&op->resource[0], 0,
|
||||
resource_size(&op->resource[0]),
|
||||
"sunbpp");
|
||||
if (!base)
|
||||
return -ENODEV;
|
||||
|
||||
size = resource_size(&op->resource[0]);
|
||||
dma = PARPORT_DMA_NONE;
|
||||
|
||||
ops = kmemdup(&parport_sunbpp_ops, sizeof(struct parport_operations),
|
||||
GFP_KERNEL);
|
||||
if (!ops)
|
||||
goto out_unmap;
|
||||
|
||||
dprintk(("register_port\n"));
|
||||
if (!(p = parport_register_port((unsigned long)base, irq, dma, ops)))
|
||||
goto out_free_ops;
|
||||
|
||||
p->size = size;
|
||||
p->dev = &op->dev;
|
||||
|
||||
if ((err = request_irq(p->irq, parport_irq_handler,
|
||||
IRQF_SHARED, p->name, p)) != 0) {
|
||||
goto out_put_port;
|
||||
}
|
||||
|
||||
parport_sunbpp_enable_irq(p);
|
||||
|
||||
regs = (struct bpp_regs __iomem *)p->base;
|
||||
|
||||
value_tcr = sbus_readb(®s->p_tcr);
|
||||
value_tcr &= ~P_TCR_DIR;
|
||||
sbus_writeb(value_tcr, ®s->p_tcr);
|
||||
|
||||
printk(KERN_INFO "%s: sunbpp at 0x%lx\n", p->name, p->base);
|
||||
|
||||
dev_set_drvdata(&op->dev, p);
|
||||
|
||||
parport_announce_port(p);
|
||||
|
||||
return 0;
|
||||
|
||||
out_put_port:
|
||||
parport_put_port(p);
|
||||
|
||||
out_free_ops:
|
||||
kfree(ops);
|
||||
|
||||
out_unmap:
|
||||
of_iounmap(&op->resource[0], base, size);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bpp_remove(struct platform_device *op)
|
||||
{
|
||||
struct parport *p = dev_get_drvdata(&op->dev);
|
||||
struct parport_operations *ops = p->ops;
|
||||
|
||||
parport_remove_port(p);
|
||||
|
||||
if (p->irq != PARPORT_IRQ_NONE) {
|
||||
parport_sunbpp_disable_irq(p);
|
||||
free_irq(p->irq, p);
|
||||
}
|
||||
|
||||
of_iounmap(&op->resource[0], (void __iomem *) p->base, p->size);
|
||||
parport_put_port(p);
|
||||
kfree(ops);
|
||||
|
||||
dev_set_drvdata(&op->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bpp_match[] = {
|
||||
{
|
||||
.name = "SUNW,bpp",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, bpp_match);
|
||||
|
||||
static struct platform_driver bpp_sbus_driver = {
|
||||
.driver = {
|
||||
.name = "bpp",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = bpp_match,
|
||||
},
|
||||
.probe = bpp_probe,
|
||||
.remove = bpp_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(bpp_sbus_driver);
|
||||
|
||||
MODULE_AUTHOR("Derrick J Brashear");
|
||||
MODULE_DESCRIPTION("Parport Driver for Sparc bidirectional Port");
|
||||
MODULE_SUPPORTED_DEVICE("Sparc Bidirectional Parallel Port");
|
||||
MODULE_VERSION("2.0");
|
||||
MODULE_LICENSE("GPL");
|
282
drivers/parport/probe.c
Normal file
282
drivers/parport/probe.c
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Parallel port device probing code
|
||||
*
|
||||
* Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
|
||||
* Philip Blundell <philb@gnu.org>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
static const struct {
|
||||
const char *token;
|
||||
const char *descr;
|
||||
} classes[] = {
|
||||
{ "", "Legacy device" },
|
||||
{ "PRINTER", "Printer" },
|
||||
{ "MODEM", "Modem" },
|
||||
{ "NET", "Network device" },
|
||||
{ "HDC", "Hard disk" },
|
||||
{ "PCMCIA", "PCMCIA" },
|
||||
{ "MEDIA", "Multimedia device" },
|
||||
{ "FDC", "Floppy disk" },
|
||||
{ "PORTS", "Ports" },
|
||||
{ "SCANNER", "Scanner" },
|
||||
{ "DIGICAM", "Digital camera" },
|
||||
{ "", "Unknown device" },
|
||||
{ "", "Unspecified" },
|
||||
{ "SCSIADAPTER", "SCSI adapter" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static void pretty_print(struct parport *port, int device)
|
||||
{
|
||||
struct parport_device_info *info = &port->probe_info[device + 1];
|
||||
|
||||
printk(KERN_INFO "%s", port->name);
|
||||
|
||||
if (device >= 0)
|
||||
printk (" (addr %d)", device);
|
||||
|
||||
printk (": %s", classes[info->class].descr);
|
||||
if (info->class)
|
||||
printk(", %s %s", info->mfr, info->model);
|
||||
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
static void parse_data(struct parport *port, int device, char *str)
|
||||
{
|
||||
char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
|
||||
char *p = txt, *q;
|
||||
int guessed_class = PARPORT_CLASS_UNSPEC;
|
||||
struct parport_device_info *info = &port->probe_info[device + 1];
|
||||
|
||||
if (!txt) {
|
||||
printk(KERN_WARNING "%s probe: memory squeeze\n", port->name);
|
||||
return;
|
||||
}
|
||||
strcpy(txt, str);
|
||||
while (p) {
|
||||
char *sep;
|
||||
q = strchr(p, ';');
|
||||
if (q) *q = 0;
|
||||
sep = strchr(p, ':');
|
||||
if (sep) {
|
||||
char *u;
|
||||
*(sep++) = 0;
|
||||
/* Get rid of trailing blanks */
|
||||
u = sep + strlen (sep) - 1;
|
||||
while (u >= p && *u == ' ')
|
||||
*u-- = '\0';
|
||||
u = p;
|
||||
while (*u) {
|
||||
*u = toupper(*u);
|
||||
u++;
|
||||
}
|
||||
if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
|
||||
kfree(info->mfr);
|
||||
info->mfr = kstrdup(sep, GFP_KERNEL);
|
||||
} else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
|
||||
kfree(info->model);
|
||||
info->model = kstrdup(sep, GFP_KERNEL);
|
||||
} else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
|
||||
int i;
|
||||
|
||||
kfree(info->class_name);
|
||||
info->class_name = kstrdup(sep, GFP_KERNEL);
|
||||
for (u = sep; *u; u++)
|
||||
*u = toupper(*u);
|
||||
for (i = 0; classes[i].token; i++) {
|
||||
if (!strcmp(classes[i].token, sep)) {
|
||||
info->class = i;
|
||||
goto rock_on;
|
||||
}
|
||||
}
|
||||
printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
|
||||
info->class = PARPORT_CLASS_OTHER;
|
||||
} else if (!strcmp(p, "CMD") ||
|
||||
!strcmp(p, "COMMAND SET")) {
|
||||
kfree(info->cmdset);
|
||||
info->cmdset = kstrdup(sep, GFP_KERNEL);
|
||||
/* if it speaks printer language, it's
|
||||
probably a printer */
|
||||
if (strstr(sep, "PJL") || strstr(sep, "PCL"))
|
||||
guessed_class = PARPORT_CLASS_PRINTER;
|
||||
} else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
|
||||
kfree(info->description);
|
||||
info->description = kstrdup(sep, GFP_KERNEL);
|
||||
}
|
||||
}
|
||||
rock_on:
|
||||
if (q)
|
||||
p = q + 1;
|
||||
else
|
||||
p = NULL;
|
||||
}
|
||||
|
||||
/* If the device didn't tell us its class, maybe we have managed to
|
||||
guess one from the things it did say. */
|
||||
if (info->class == PARPORT_CLASS_UNSPEC)
|
||||
info->class = guessed_class;
|
||||
|
||||
pretty_print (port, device);
|
||||
|
||||
kfree(txt);
|
||||
}
|
||||
|
||||
/* Read up to count-1 bytes of device id. Terminate buffer with
|
||||
* '\0'. Buffer begins with two Device ID length bytes as given by
|
||||
* device. */
|
||||
static ssize_t parport_read_device_id (struct parport *port, char *buffer,
|
||||
size_t count)
|
||||
{
|
||||
unsigned char length[2];
|
||||
unsigned lelen, belen;
|
||||
size_t idlens[4];
|
||||
unsigned numidlens;
|
||||
unsigned current_idlen;
|
||||
ssize_t retval;
|
||||
size_t len;
|
||||
|
||||
/* First two bytes are MSB,LSB of inclusive length. */
|
||||
retval = parport_read (port, length, 2);
|
||||
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
if (retval != 2)
|
||||
return -EIO;
|
||||
|
||||
if (count < 2)
|
||||
return 0;
|
||||
memcpy(buffer, length, 2);
|
||||
len = 2;
|
||||
|
||||
/* Some devices wrongly send LE length, and some send it two
|
||||
* bytes short. Construct a sorted array of lengths to try. */
|
||||
belen = (length[0] << 8) + length[1];
|
||||
lelen = (length[1] << 8) + length[0];
|
||||
idlens[0] = min(belen, lelen);
|
||||
idlens[1] = idlens[0]+2;
|
||||
if (belen != lelen) {
|
||||
int off = 2;
|
||||
/* Don't try lengths of 0x100 and 0x200 as 1 and 2 */
|
||||
if (idlens[0] <= 2)
|
||||
off = 0;
|
||||
idlens[off] = max(belen, lelen);
|
||||
idlens[off+1] = idlens[off]+2;
|
||||
numidlens = off+2;
|
||||
}
|
||||
else {
|
||||
/* Some devices don't truly implement Device ID, but
|
||||
* just return constant nibble forever. This catches
|
||||
* also those cases. */
|
||||
if (idlens[0] == 0 || idlens[0] > 0xFFF) {
|
||||
printk (KERN_DEBUG "%s: reported broken Device ID"
|
||||
" length of %#zX bytes\n",
|
||||
port->name, idlens[0]);
|
||||
return -EIO;
|
||||
}
|
||||
numidlens = 2;
|
||||
}
|
||||
|
||||
/* Try to respect the given ID length despite all the bugs in
|
||||
* the ID length. Read according to shortest possible ID
|
||||
* first. */
|
||||
for (current_idlen = 0; current_idlen < numidlens; ++current_idlen) {
|
||||
size_t idlen = idlens[current_idlen];
|
||||
if (idlen+1 >= count)
|
||||
break;
|
||||
|
||||
retval = parport_read (port, buffer+len, idlen-len);
|
||||
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
len += retval;
|
||||
|
||||
if (port->physport->ieee1284.phase != IEEE1284_PH_HBUSY_DAVAIL) {
|
||||
if (belen != len) {
|
||||
printk (KERN_DEBUG "%s: Device ID was %zd bytes"
|
||||
" while device told it would be %d"
|
||||
" bytes\n",
|
||||
port->name, len, belen);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* This might end reading the Device ID too
|
||||
* soon. Hopefully the needed fields were already in
|
||||
* the first 256 bytes or so that we must have read so
|
||||
* far. */
|
||||
if (buffer[len-1] == ';') {
|
||||
printk (KERN_DEBUG "%s: Device ID reading stopped"
|
||||
" before device told data not available. "
|
||||
"Current idlen %u of %u, len bytes %02X %02X\n",
|
||||
port->name, current_idlen, numidlens,
|
||||
length[0], length[1]);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (current_idlen < numidlens) {
|
||||
/* Buffer not large enough, read to end of buffer. */
|
||||
size_t idlen, len2;
|
||||
if (len+1 < count) {
|
||||
retval = parport_read (port, buffer+len, count-len-1);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
len += retval;
|
||||
}
|
||||
/* Read the whole ID since some devices would not
|
||||
* otherwise give back the Device ID from beginning
|
||||
* next time when asked. */
|
||||
idlen = idlens[current_idlen];
|
||||
len2 = len;
|
||||
while(len2 < idlen && retval > 0) {
|
||||
char tmp[4];
|
||||
retval = parport_read (port, tmp,
|
||||
min(sizeof tmp, idlen-len2));
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
len2 += retval;
|
||||
}
|
||||
}
|
||||
/* In addition, there are broken devices out there that don't
|
||||
even finish off with a semi-colon. We do not need to care
|
||||
about those at this time. */
|
||||
done:
|
||||
buffer[len] = '\0';
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Get Std 1284 Device ID. */
|
||||
ssize_t parport_device_id (int devnum, char *buffer, size_t count)
|
||||
{
|
||||
ssize_t retval = -ENXIO;
|
||||
struct pardevice *dev = parport_open (devnum, "Device ID probe");
|
||||
if (!dev)
|
||||
return -ENXIO;
|
||||
|
||||
parport_claim_or_block (dev);
|
||||
|
||||
/* Negotiate to compatibility mode, and then to device ID
|
||||
* mode. (This so that we start form beginning of device ID if
|
||||
* already in device ID mode.) */
|
||||
parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
|
||||
retval = parport_negotiate (dev->port,
|
||||
IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID);
|
||||
|
||||
if (!retval) {
|
||||
retval = parport_read_device_id (dev->port, buffer, count);
|
||||
parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
|
||||
if (retval > 2)
|
||||
parse_data (dev->port, dev->daisy, buffer+2);
|
||||
}
|
||||
|
||||
parport_release (dev);
|
||||
parport_close (dev);
|
||||
return retval;
|
||||
}
|
608
drivers/parport/procfs.c
Normal file
608
drivers/parport/procfs.c
Normal file
|
@ -0,0 +1,608 @@
|
|||
/* Sysctl interface for parport devices.
|
||||
*
|
||||
* Authors: David Campbell
|
||||
* Tim Waugh <tim@cyberelk.demon.co.uk>
|
||||
* Philip Blundell <philb@gnu.org>
|
||||
* Andrea Arcangeli
|
||||
* Riccardo Facchetti <fizban@tin.it>
|
||||
*
|
||||
* based on work by Grant Guenther <grant@torque.net>
|
||||
* and Philip Blundell
|
||||
*
|
||||
* Cleaned up include files - Russell King <linux@arm.uk.linux.org>
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
|
||||
|
||||
#define PARPORT_MIN_TIMESLICE_VALUE 1ul
|
||||
#define PARPORT_MAX_TIMESLICE_VALUE ((unsigned long) HZ)
|
||||
#define PARPORT_MIN_SPINTIME_VALUE 1
|
||||
#define PARPORT_MAX_SPINTIME_VALUE 1000
|
||||
|
||||
static int do_active_device(struct ctl_table *table, int write,
|
||||
void __user *result, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct parport *port = (struct parport *)table->extra1;
|
||||
char buffer[256];
|
||||
struct pardevice *dev;
|
||||
int len = 0;
|
||||
|
||||
if (write) /* can't happen anyway */
|
||||
return -EACCES;
|
||||
|
||||
if (*ppos) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (dev = port->devices; dev ; dev = dev->next) {
|
||||
if(dev == port->cad) {
|
||||
len += sprintf(buffer, "%s\n", dev->name);
|
||||
}
|
||||
}
|
||||
|
||||
if(!len) {
|
||||
len += sprintf(buffer, "%s\n", "none");
|
||||
}
|
||||
|
||||
if (len > *lenp)
|
||||
len = *lenp;
|
||||
else
|
||||
*lenp = len;
|
||||
|
||||
*ppos += len;
|
||||
|
||||
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PARPORT_1284
|
||||
static int do_autoprobe(struct ctl_table *table, int write,
|
||||
void __user *result, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct parport_device_info *info = table->extra2;
|
||||
const char *str;
|
||||
char buffer[256];
|
||||
int len = 0;
|
||||
|
||||
if (write) /* permissions stop this */
|
||||
return -EACCES;
|
||||
|
||||
if (*ppos) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((str = info->class_name) != NULL)
|
||||
len += sprintf (buffer + len, "CLASS:%s;\n", str);
|
||||
|
||||
if ((str = info->model) != NULL)
|
||||
len += sprintf (buffer + len, "MODEL:%s;\n", str);
|
||||
|
||||
if ((str = info->mfr) != NULL)
|
||||
len += sprintf (buffer + len, "MANUFACTURER:%s;\n", str);
|
||||
|
||||
if ((str = info->description) != NULL)
|
||||
len += sprintf (buffer + len, "DESCRIPTION:%s;\n", str);
|
||||
|
||||
if ((str = info->cmdset) != NULL)
|
||||
len += sprintf (buffer + len, "COMMAND SET:%s;\n", str);
|
||||
|
||||
if (len > *lenp)
|
||||
len = *lenp;
|
||||
else
|
||||
*lenp = len;
|
||||
|
||||
*ppos += len;
|
||||
|
||||
return copy_to_user (result, buffer, len) ? -EFAULT : 0;
|
||||
}
|
||||
#endif /* IEEE1284.3 support. */
|
||||
|
||||
static int do_hardware_base_addr(struct ctl_table *table, int write,
|
||||
void __user *result,
|
||||
size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct parport *port = (struct parport *)table->extra1;
|
||||
char buffer[20];
|
||||
int len = 0;
|
||||
|
||||
if (*ppos) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (write) /* permissions prevent this anyway */
|
||||
return -EACCES;
|
||||
|
||||
len += sprintf (buffer, "%lu\t%lu\n", port->base, port->base_hi);
|
||||
|
||||
if (len > *lenp)
|
||||
len = *lenp;
|
||||
else
|
||||
*lenp = len;
|
||||
|
||||
*ppos += len;
|
||||
|
||||
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int do_hardware_irq(struct ctl_table *table, int write,
|
||||
void __user *result,
|
||||
size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct parport *port = (struct parport *)table->extra1;
|
||||
char buffer[20];
|
||||
int len = 0;
|
||||
|
||||
if (*ppos) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (write) /* permissions prevent this anyway */
|
||||
return -EACCES;
|
||||
|
||||
len += sprintf (buffer, "%d\n", port->irq);
|
||||
|
||||
if (len > *lenp)
|
||||
len = *lenp;
|
||||
else
|
||||
*lenp = len;
|
||||
|
||||
*ppos += len;
|
||||
|
||||
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int do_hardware_dma(struct ctl_table *table, int write,
|
||||
void __user *result,
|
||||
size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct parport *port = (struct parport *)table->extra1;
|
||||
char buffer[20];
|
||||
int len = 0;
|
||||
|
||||
if (*ppos) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (write) /* permissions prevent this anyway */
|
||||
return -EACCES;
|
||||
|
||||
len += sprintf (buffer, "%d\n", port->dma);
|
||||
|
||||
if (len > *lenp)
|
||||
len = *lenp;
|
||||
else
|
||||
*lenp = len;
|
||||
|
||||
*ppos += len;
|
||||
|
||||
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int do_hardware_modes(struct ctl_table *table, int write,
|
||||
void __user *result,
|
||||
size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct parport *port = (struct parport *)table->extra1;
|
||||
char buffer[40];
|
||||
int len = 0;
|
||||
|
||||
if (*ppos) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (write) /* permissions prevent this anyway */
|
||||
return -EACCES;
|
||||
|
||||
{
|
||||
#define printmode(x) {if(port->modes&PARPORT_MODE_##x){len+=sprintf(buffer+len,"%s%s",f?",":"",#x);f++;}}
|
||||
int f = 0;
|
||||
printmode(PCSPP);
|
||||
printmode(TRISTATE);
|
||||
printmode(COMPAT);
|
||||
printmode(EPP);
|
||||
printmode(ECP);
|
||||
printmode(DMA);
|
||||
#undef printmode
|
||||
}
|
||||
buffer[len++] = '\n';
|
||||
|
||||
if (len > *lenp)
|
||||
len = *lenp;
|
||||
else
|
||||
*lenp = len;
|
||||
|
||||
*ppos += len;
|
||||
|
||||
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
#define PARPORT_PORT_DIR(CHILD) { .procname = NULL, .mode = 0555, .child = CHILD }
|
||||
#define PARPORT_PARPORT_DIR(CHILD) { .procname = "parport", \
|
||||
.mode = 0555, .child = CHILD }
|
||||
#define PARPORT_DEV_DIR(CHILD) { .procname = "dev", .mode = 0555, .child = CHILD }
|
||||
#define PARPORT_DEVICES_ROOT_DIR { .procname = "devices", \
|
||||
.mode = 0555, .child = NULL }
|
||||
|
||||
static const unsigned long parport_min_timeslice_value =
|
||||
PARPORT_MIN_TIMESLICE_VALUE;
|
||||
|
||||
static const unsigned long parport_max_timeslice_value =
|
||||
PARPORT_MAX_TIMESLICE_VALUE;
|
||||
|
||||
static const int parport_min_spintime_value =
|
||||
PARPORT_MIN_SPINTIME_VALUE;
|
||||
|
||||
static const int parport_max_spintime_value =
|
||||
PARPORT_MAX_SPINTIME_VALUE;
|
||||
|
||||
|
||||
struct parport_sysctl_table {
|
||||
struct ctl_table_header *sysctl_header;
|
||||
struct ctl_table vars[12];
|
||||
struct ctl_table device_dir[2];
|
||||
struct ctl_table port_dir[2];
|
||||
struct ctl_table parport_dir[2];
|
||||
struct ctl_table dev_dir[2];
|
||||
};
|
||||
|
||||
static const struct parport_sysctl_table parport_sysctl_template = {
|
||||
.sysctl_header = NULL,
|
||||
{
|
||||
{
|
||||
.procname = "spintime",
|
||||
.data = NULL,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = (void*) &parport_min_spintime_value,
|
||||
.extra2 = (void*) &parport_max_spintime_value
|
||||
},
|
||||
{
|
||||
.procname = "base-addr",
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0444,
|
||||
.proc_handler = do_hardware_base_addr
|
||||
},
|
||||
{
|
||||
.procname = "irq",
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0444,
|
||||
.proc_handler = do_hardware_irq
|
||||
},
|
||||
{
|
||||
.procname = "dma",
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0444,
|
||||
.proc_handler = do_hardware_dma
|
||||
},
|
||||
{
|
||||
.procname = "modes",
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0444,
|
||||
.proc_handler = do_hardware_modes
|
||||
},
|
||||
PARPORT_DEVICES_ROOT_DIR,
|
||||
#ifdef CONFIG_PARPORT_1284
|
||||
{
|
||||
.procname = "autoprobe",
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0444,
|
||||
.proc_handler = do_autoprobe
|
||||
},
|
||||
{
|
||||
.procname = "autoprobe0",
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0444,
|
||||
.proc_handler = do_autoprobe
|
||||
},
|
||||
{
|
||||
.procname = "autoprobe1",
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0444,
|
||||
.proc_handler = do_autoprobe
|
||||
},
|
||||
{
|
||||
.procname = "autoprobe2",
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0444,
|
||||
.proc_handler = do_autoprobe
|
||||
},
|
||||
{
|
||||
.procname = "autoprobe3",
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0444,
|
||||
.proc_handler = do_autoprobe
|
||||
},
|
||||
#endif /* IEEE 1284 support */
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
.procname = "active",
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0444,
|
||||
.proc_handler = do_active_device
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
PARPORT_PORT_DIR(NULL),
|
||||
{}
|
||||
},
|
||||
{
|
||||
PARPORT_PARPORT_DIR(NULL),
|
||||
{}
|
||||
},
|
||||
{
|
||||
PARPORT_DEV_DIR(NULL),
|
||||
{}
|
||||
}
|
||||
};
|
||||
|
||||
struct parport_device_sysctl_table
|
||||
{
|
||||
struct ctl_table_header *sysctl_header;
|
||||
struct ctl_table vars[2];
|
||||
struct ctl_table device_dir[2];
|
||||
struct ctl_table devices_root_dir[2];
|
||||
struct ctl_table port_dir[2];
|
||||
struct ctl_table parport_dir[2];
|
||||
struct ctl_table dev_dir[2];
|
||||
};
|
||||
|
||||
static const struct parport_device_sysctl_table
|
||||
parport_device_sysctl_template = {
|
||||
.sysctl_header = NULL,
|
||||
{
|
||||
{
|
||||
.procname = "timeslice",
|
||||
.data = NULL,
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_doulongvec_ms_jiffies_minmax,
|
||||
.extra1 = (void*) &parport_min_timeslice_value,
|
||||
.extra2 = (void*) &parport_max_timeslice_value
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
.procname = NULL,
|
||||
.data = NULL,
|
||||
.maxlen = 0,
|
||||
.mode = 0555,
|
||||
.child = NULL
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
PARPORT_DEVICES_ROOT_DIR,
|
||||
{}
|
||||
},
|
||||
{
|
||||
PARPORT_PORT_DIR(NULL),
|
||||
{}
|
||||
},
|
||||
{
|
||||
PARPORT_PARPORT_DIR(NULL),
|
||||
{}
|
||||
},
|
||||
{
|
||||
PARPORT_DEV_DIR(NULL),
|
||||
{}
|
||||
}
|
||||
};
|
||||
|
||||
struct parport_default_sysctl_table
|
||||
{
|
||||
struct ctl_table_header *sysctl_header;
|
||||
struct ctl_table vars[3];
|
||||
struct ctl_table default_dir[2];
|
||||
struct ctl_table parport_dir[2];
|
||||
struct ctl_table dev_dir[2];
|
||||
};
|
||||
|
||||
static struct parport_default_sysctl_table
|
||||
parport_default_sysctl_table = {
|
||||
.sysctl_header = NULL,
|
||||
{
|
||||
{
|
||||
.procname = "timeslice",
|
||||
.data = &parport_default_timeslice,
|
||||
.maxlen = sizeof(parport_default_timeslice),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_doulongvec_ms_jiffies_minmax,
|
||||
.extra1 = (void*) &parport_min_timeslice_value,
|
||||
.extra2 = (void*) &parport_max_timeslice_value
|
||||
},
|
||||
{
|
||||
.procname = "spintime",
|
||||
.data = &parport_default_spintime,
|
||||
.maxlen = sizeof(parport_default_spintime),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = (void*) &parport_min_spintime_value,
|
||||
.extra2 = (void*) &parport_max_spintime_value
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
.procname = "default",
|
||||
.mode = 0555,
|
||||
.child = parport_default_sysctl_table.vars
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
PARPORT_PARPORT_DIR(parport_default_sysctl_table.default_dir),
|
||||
{}
|
||||
},
|
||||
{
|
||||
PARPORT_DEV_DIR(parport_default_sysctl_table.parport_dir),
|
||||
{}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int parport_proc_register(struct parport *port)
|
||||
{
|
||||
struct parport_sysctl_table *t;
|
||||
int i;
|
||||
|
||||
t = kmemdup(&parport_sysctl_template, sizeof(*t), GFP_KERNEL);
|
||||
if (t == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
t->device_dir[0].extra1 = port;
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
t->vars[i].extra1 = port;
|
||||
|
||||
t->vars[0].data = &port->spintime;
|
||||
t->vars[5].child = t->device_dir;
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
t->vars[6 + i].extra2 = &port->probe_info[i];
|
||||
|
||||
t->port_dir[0].procname = port->name;
|
||||
|
||||
t->port_dir[0].child = t->vars;
|
||||
t->parport_dir[0].child = t->port_dir;
|
||||
t->dev_dir[0].child = t->parport_dir;
|
||||
|
||||
t->sysctl_header = register_sysctl_table(t->dev_dir);
|
||||
if (t->sysctl_header == NULL) {
|
||||
kfree(t);
|
||||
t = NULL;
|
||||
}
|
||||
port->sysctl_table = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parport_proc_unregister(struct parport *port)
|
||||
{
|
||||
if (port->sysctl_table) {
|
||||
struct parport_sysctl_table *t = port->sysctl_table;
|
||||
port->sysctl_table = NULL;
|
||||
unregister_sysctl_table(t->sysctl_header);
|
||||
kfree(t);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parport_device_proc_register(struct pardevice *device)
|
||||
{
|
||||
struct parport_device_sysctl_table *t;
|
||||
struct parport * port = device->port;
|
||||
|
||||
t = kmemdup(&parport_device_sysctl_template, sizeof(*t), GFP_KERNEL);
|
||||
if (t == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
t->dev_dir[0].child = t->parport_dir;
|
||||
t->parport_dir[0].child = t->port_dir;
|
||||
t->port_dir[0].procname = port->name;
|
||||
t->port_dir[0].child = t->devices_root_dir;
|
||||
t->devices_root_dir[0].child = t->device_dir;
|
||||
|
||||
t->device_dir[0].procname = device->name;
|
||||
t->device_dir[0].child = t->vars;
|
||||
t->vars[0].data = &device->timeslice;
|
||||
|
||||
t->sysctl_header = register_sysctl_table(t->dev_dir);
|
||||
if (t->sysctl_header == NULL) {
|
||||
kfree(t);
|
||||
t = NULL;
|
||||
}
|
||||
device->sysctl_table = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parport_device_proc_unregister(struct pardevice *device)
|
||||
{
|
||||
if (device->sysctl_table) {
|
||||
struct parport_device_sysctl_table *t = device->sysctl_table;
|
||||
device->sysctl_table = NULL;
|
||||
unregister_sysctl_table(t->sysctl_header);
|
||||
kfree(t);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init parport_default_proc_register(void)
|
||||
{
|
||||
parport_default_sysctl_table.sysctl_header =
|
||||
register_sysctl_table(parport_default_sysctl_table.dev_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit parport_default_proc_unregister(void)
|
||||
{
|
||||
if (parport_default_sysctl_table.sysctl_header) {
|
||||
unregister_sysctl_table(parport_default_sysctl_table.
|
||||
sysctl_header);
|
||||
parport_default_sysctl_table.sysctl_header = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#else /* no sysctl or no procfs*/
|
||||
|
||||
int parport_proc_register(struct parport *pp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parport_proc_unregister(struct parport *pp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parport_device_proc_register(struct pardevice *device)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parport_device_proc_unregister(struct pardevice *device)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init parport_default_proc_register (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit parport_default_proc_unregister (void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
module_init(parport_default_proc_register)
|
||||
module_exit(parport_default_proc_unregister)
|
1032
drivers/parport/share.c
Normal file
1032
drivers/parport/share.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue