/*-
* Copyright (c) 1997 Nicolas Souchu
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* FreeBSD: src/sys/dev/ppbus/ppb_1284.c,v 1.11 2000/01/14 08:03:14 nsouch Exp
*
*/
/* General purpose routines for the IEEE1284-1994 Standard */
/* Wait for the peripheral up to 40ms */
static int
do_1284_wait(struct ppbus_softc * bus, char mask, char status)
{
return (ppbus_poll_bus(bus->sc_dev, 4, mask, status,
PPBUS_NOINTR | PPBUS_POLL));
}
/* Wait for the host up to 1 second (peripheral side) */
static int
do_peripheral_wait(struct ppbus_softc * bus, char mask, char status)
{
return (ppbus_poll_bus(bus->sc_dev, 100, mask, status,
PPBUS_NOINTR | PPBUS_POLL));
}
/* Unconditionally reset the error field */
static int
ppbus_1284_reset_error(struct ppbus_softc * bus, int state)
{
bus->sc_1284_error = PPBUS_NO_ERROR;
bus->sc_1284_state = state;
return 0;
}
/* Get IEEE1284 state */
int
ppbus_1284_get_state(device_t dev)
{
struct ppbus_softc *sc = device_private(dev);
return sc->sc_1284_state;
}
/* Set IEEE1284 state if no error occurred */
int
ppbus_1284_set_state(device_t dev, int state)
{
struct ppbus_softc * bus = device_private(dev);
/* call ppbus_1284_reset_error() if you absolutly want to change
* the state from PPBUS_ERROR to another */
if ((bus->sc_1284_state != PPBUS_ERROR) &&
(bus->sc_1284_error == PPBUS_NO_ERROR)) {
bus->sc_1284_state = state;
bus->sc_1284_error = PPBUS_NO_ERROR;
}
return 0;
}
/* Set the IEEE1284 error field */
static int
ppbus_1284_set_error(struct ppbus_softc * bus, int error, int event)
{
/* do not accumulate errors */
if ((bus->sc_1284_error == PPBUS_NO_ERROR) &&
(bus->sc_1284_state != PPBUS_ERROR)) {
bus->sc_1284_error = error;
bus->sc_1284_state = PPBUS_ERROR;
}
/* Negotiate the peripheral side */
int
ppbus_peripheral_negotiate(device_t dev, int mode, int options)
{
struct ppbus_softc * bus = device_private(dev);
int spin, request_mode, error = 0;
char r;
/* Terminate peripheral transfer side. Always return 0 in compatible mode */
int
ppbus_peripheral_terminate(device_t dev, int how)
{
struct ppbus_softc * bus = device_private(dev);
int error = 0;
/* Write n bytes to host in BYTE mode (peripheral side) */
int
byte_peripheral_write(device_t dev, char *buffer, int len,
int *sent)
{
int error = 0, i;
char r;
/* wait forever, the remote host is master and should initiate
* termination
*/
for(i = 0; i < len; i++) {
/* force remote nFAULT low to release the remote waiting
* process, if any
*/
r = ppbus_rctr(dev);
ppbus_wctr(dev, r & ~nINIT);
#ifdef DEBUG_1284
printf("b");
#endif
if ((error = byte_peripheral_outbyte(dev, buffer+i, (i == len-1))))
goto error;
}
error:
if (!error)
ppbus_1284_set_state(dev, PPBUS_PERIPHERAL_IDLE);
*sent = i;
return (error);
}
/* Read the device ID using the specified mode */
int
ppbus_1284_read_id(device_t dev, int mode, char ** buffer,
size_t * size, size_t * read)
{
u_int16_t msg_sz;
u_int8_t length_field;
u_int8_t old_mode;
int error;
int old_ivar;
int new_ivar = 1;
/*
* IEEE1284 negotiation phase: after negotiation, nFAULT is low if data is
* available for reverse modes.
*/
int
ppbus_1284_negotiate(device_t dev, int mode, int options)
{
struct ppbus_softc * bus = device_private(dev);
int error;
int request_mode;
#ifdef DEBUG_1284
printf("n");
#endif
if (ppbus_1284_get_state(dev) >= PPBUS_PERIPHERAL_NEGOTIATION)
ppbus_peripheral_terminate(dev, PPBUS_WAIT);
#ifdef DEBUG_1284
printf("%d", mode);
#endif
/* ensure the host is in compatible mode */
ppbus_1284_terminate(dev);
/* reset error to catch the actual negotiation error */
ppbus_1284_reset_error(bus, PPBUS_FORWARD_IDLE);
/* calculate ext. value */
request_mode = ppbus_request_mode(mode, options);
/* ok, the host enters the ForwardIdle state */
ppbus_1284_set_state(dev, PPBUS_ECP_FORWARD_IDLE);
break;
case PPBUS_EPP:
ppbus_1284_set_state(dev, PPBUS_EPP_IDLE);
break;
default:
panic("%s: unknown mode (%d)!", __func__, mode);
}
return 0;
error:
ppbus_1284_terminate(dev);
return error;
}
/*
* IEEE1284 termination phase, return code should ignored since the host
* is _always_ in compatible mode after ppbus_1284_terminate()
*/
int
ppbus_1284_terminate(device_t dev)
{
struct ppbus_softc * bus = device_private(dev);
#ifdef DEBUG_1284
printf("T");
#endif
/* do not reset error here to keep the error that
* may occurred before the ppbus_1284_terminate() call */
ppbus_1284_set_state(dev, PPBUS_TERMINATION);