schreite@helena.physik.uni-stuttgart.de
Thu, 01 Oct 98 12:04:19 +0100
Kernel 2.1.123:
diff -u6:
--- parport_ieee1284.c.orig Wed Sep 30 12:14:53 1998
+++ parport_ieee1284.c.inter Thu Oct 1 12:45:23 1998
@@ -1,18 +1,20 @@
/* $Id: parport_ieee1284.c,v 1.4 1997/10/19 21:37:21 philip Exp $
* IEEE-1284 implementation for parport.
*
* Authors: Phil Blundell <Philip.Blundell@pobox.com>
* Carsten Gross <carsten@sol.wohnheim.uni-ulm.de>
* Jose Renau <renau@acm.org>
+ * Roger Schreiter <Roger.Schreiter@t-online.de>
*/
#include <linux/tasks.h>
#include <linux/parport.h>
#include <linux/delay.h>
#include <linux/kernel.h>
+#include <linux/lp.h>
/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to
* 25 for this. After this time we can create a timeout because the
* peripheral doesn't conform to IEEE1284. We want to save CPU time: we are
* waiting a maximum time of 500 us busy (this is for speed). If there is
* not the right answer in this time, we call schedule and other processes
@@ -37,20 +39,30 @@
current->timeout = jiffies+4;
schedule(); /* wait for 40ms */
status = parport_read_status(port);
return ((status & mask) == result)?0:1;
}
+
+/**********************************
+ *** negotiating ieee1284_modes ***
+ **********************************/
+
/* Test if the peripheral is IEEE 1284 compliant.
* return values are:
* 0 - handshake failed; peripheral is not compliant (or none present)
* 1 - handshake OK; IEEE1284 peripheral present but no data available
* 2 - handshake OK; IEEE1284 peripheral and data available
+ *
+ * This function will be obsolete, when parport_ieee1284_negotiate
+ * will have been sufficiently tested
*/
int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode)
{
+ if (port->cad) port->cad->ieee1284.current_mode=IEEE1284_NONE;
+
parport_write_data(port, mode);
udelay(500);
/* nSelectIn high, nAutoFd low */
parport_write_control(port, (parport_read_control(port) & ~8) | 2);
if (parport_wait_peripheral(port, 0x78, 0x38)) {
parport_write_control(port,
@@ -62,8 +74,792 @@
udelay(5); /* Strobe wait */
/* nStrobe high */
parport_write_control(port, parport_read_control(port) & ~1);
udelay(5);
/* nAutoFd low */
parport_write_control(port, parport_read_control(port) & ~2);
+ if (port->cad) port->cad->ieee1284.current_mode=IEEE1284_NIBBLE_MODE_BIT;
return (parport_wait_peripheral(port, 0x20, 0))?2:1;
}
+
+
+/***************************************************************************
+ * Below here: "coplain" to Roger Schreiter, if there are bugs!
+ *
+ * Brief manual:
+ * These functions provide a general interface to access IEEE1284
+ * modes via the following functions:
+ * - parport_ieee1284_write_block
+ * - parport_ieee1284_read_block
+ * - parport_ieee1284_change_channel
+ * - parport_ieee1284_query_state
+ * - parport_ieee1284_set_modemask
+ * - parport_ieee1284_set_mode
+ *
+ * (The use of the functions parport_ieee1284_negotiate and
+ * parport_quit_ieee1284_modes is also possible, perhaps useful
+ * for releasing a peripheral.)
+ *
+ * This code is n o t ready:
+ * Nibble and byte mode are well supported. ECP is supported for
+ * PS2 parports (ECP emulated by software). The compatibilty mode,
+ * EPP and ECP via ECP-parports aren't yet support, the latter will
+ * follow soon!
+ *
+ * Now more details:
+ * For the arguements and return values see at the function code itsself!
+ * Sometimes you will find further hints there or in parport.h.
+ *
+ * One feature of this code is, that it can be run atomically (see below).
+ * That's why you must provide the struct parport_argument_packet for
+ * almost every function. If you don't intend to run functions atomically
+ * and if you don't mind, whether those functions call schedule, you needn't
+ * bother about that struct parport_argument_packet: Just declare and
+ * initialize in your code:
+ * struct parport_argument_packet data=EMPTY_PARPORT_ARGUMENT_PACKET;
+ * and set &data everywhere, where a pointer to such a struct is expected!
+ *
+ * When initialized, just use parport_write_block,
+ * parport_read_block and parport_change_channel (ECP/EPP) to
+ * communicate to your peripheral. The write and read functions will
+ * chose the appropriate IEEE1284 mode and will perform a negotiation
+ * is necessary. (Channel_change won't change the IEEE1284 mode, but
+ * report an error, if called within a mode without channels/addresses.)
+ * parport_query_state will return, whether there is an communication
+ * error condition or whether there are reverse data available.
+ * Errors are cleared by the call of parport_ieee1284_set_mode.
+ *
+ * In order to use those functions two things must be done first:
+ * First: allow certain IEEE1284 modes:
+ * unsigned int parport_ieee1284_set_modemask(struct parport *port,
+ * unsigned int mask,
+ * int flags)
+ * The low byte of mask is for the forward channel, the next
+ * byte for the reverse channel.
+ * Use mask=0xffff to allow all available IEEE1284 modes.
+ * If you didn't previously load the module parport_probe with the
+ * line "#define PROBE_FOR_IEEE1284_MODES" uncommented, you must use
+ * the flag PARPORT_IGNORE_PROBE_INFO and mask exactly the IEEE1284
+ * modes which are really available within your peripheral.
+ * Further flags are explained in parport.h and here at the beginning
+ * of the function code.
+ *
+ * Second: negotiate and init with:
+ * int parport_ieee1284_set_mode(struct parport *port, unsigned char dir,
+ * struct parport_argument_packet *data)
+ * dir can be PARPORT_FORWARD, PARPORT_REVERSE, both ord or 0. In almost
+ * every case dir=0 should work fine.
+ *
+ * A more detailed manual will follow soon!
+ *
+ * Running functions atomically:
+ * This is very useful when using those functions in socket code or
+ * driven by interrupts, ... .
+ *
+ * Just set in the struct parport_argument_packet, which will be called
+ * "data" in the following, the member mode to PAPORT_ATOMIC (see
+ * parport.h!). Then the called function won't call schedule, when your
+ * peripheral is busy, but return (with the return value
+ * "PARPORT_TRYING_AGAIN", if any) and carry out the rest of its task via
+ * taks queues.
+ *
+ * Probably you want to be informed, when the work is done. Therefore:
+ * Put a call back function in data.call_back and maybe "your data" in
+ * data.driver_data. This call back function will be called with data
+ * as argument (not driver_data, which is inside data!), when the work
+ * is done or an error occured. The return value (result or error code)
+ * will be found in data.result. Read data can be found in data.buffer.
+ *
+ * But be careful: In order to be fast, your originally function call
+ * will try to do as much work as possible - task queues are used, when
+ * your peripheral was busy or the sum wait time was alomst a tenth of a
+ * second. So be prepared, that your call back function is called before
+ * your originally function call is returned. Also your call back
+ * function may not sleep!
+ *
+ * Pay always attention which parport_argument_packets are in use.
+ * Do not reuse them before they are returned to your call back function!
+ * If you want to get back a parport_argument_packet immedeately, write
+ * PARPORT_ABORT to its mode (data.mode) and wait until it will soon
+ * return (call your call back function) with an error code.
+ * This may be useful for cleaning up a module.
+ *
+ * More expert:
+ * Perhaps you already mentionned, that most of the above described
+ * functions just call a similar named function ending on "_p".
+ * This is, because we need functions with unique arguments - those
+ * _p-functions just take data as argument and return nothing (just like
+ * your call back function). You also can use those "simplified"
+ * function calls - look below for the initialization of the data-members!
+ *
+ * If you have a really fast peripheral which reads or writes huge
+ * amounts of data without getting busy, it is useful to use those
+ * simplified functions calls, but not reset data.sum_delay to 0!
+ * Then you can be sure, that every tenth second other processes can
+ * run and won't hang for a too long time, because schedule is no more
+ * called.
+ */
+
+void parport_nop(void *nothing)
+{
+ return;
+}
+
+
+#define DPORT data->port
+#define DDATA data->data
+#define DSTATUS data->status
+#define DRESULT data->result
+#define DSAVE_RESULT data->save_result
+#define TIMEOUT -EIO
+#define OK 0
+#define TRYING_AGAIN PARPORT_TRYING_AGAIN
+#define UDELAY(usec) { data->sum_delay+=(usec); udelay(usec); }
+#define MAX_WAIT_SHORT 20
+#define MAX_SUM_DELAY 10000
+int parport_wait_status(struct parport_argument_packet *data,
+ unsigned char mask, unsigned char expect, int timeout)
+{
+ int wait_short;
+
+ if (!data->wait_short) data->timeout_absolute=jiffies+timeout;
+ for (wait_short=data->wait_short; wait_short < MAX_WAIT_SHORT;
+ wait_short++) {
+ if (data->mode==PARPORT_ABORT) return TIMEOUT;
+ if (data->sum_delay<MAX_SUM_DELAY) {
+ DSTATUS=parport_read_status(DPORT);
+ if ((DSTATUS&mask)==expect)
+ return OK;
+ if (jiffies>=data->timeout_absolute) break;
+ UDELAY(25);
+ }
+ if ( (current->need_resched) ||
+ (data->sum_delay>=MAX_SUM_DELAY) ) {
+ if (data->sum_delay<MAX_SUM_DELAY) {
+ DSTATUS=parport_read_status(DPORT);
+ if ((DSTATUS&mask)==expect)
+ return OK;
+ }
+ if (data->mode==PARPORT_MAY_SLEEP)
+ { schedule(); data->sum_delay=0; }
+ else {
+ data->wait_short=wait_short+1;
+ (void(*)(struct parport_argument_packet*))
+ (data->tq.routine)=
+ data->in_function[data->depth];
+ data->tq.data=data;
+ data->sum_delay=0;
+ queue_task(&(data->tq), &tq_scheduler);
+ return TRYING_AGAIN;
+ }
+ }
+ }
+
+ if (data->mode==PARPORT_ABORT) return TIMEOUT;
+ if (data->mode==PARPORT_ATOMIC) {
+ if (((DSTATUS=parport_read_status(DPORT))&mask)==expect)
+ return OK;
+ if (jiffies>=data->timeout_absolute)
+ return TIMEOUT;
+
+ (void(*)(struct parport_argument_packet*))
+ (data->tq.routine)=
+ data->in_function[data->depth];
+ data->tq.data=data;
+ data->sum_delay=0;
+ queue_task(&(data->tq), &tq_scheduler);
+ return TRYING_AGAIN;
+ } else {
+ current->state=TASK_INTERRUPTIBLE;
+ current->timeout=data->timeout_absolute;
+ schedule();
+ data->sum_delay=0;
+
+ DSTATUS = parport_read_status(DPORT);
+ return ((DSTATUS & mask) == expect)?OK:TIMEOUT;
+ }
+}
+
+
+#define WRITE_CONTROL(value) { data->lastcontrol=value; \
+ parport_write_control(DPORT, value); }
+#define SET_STROBE parport_write_control(DPORT, data->lastcontrol| \
+ PARPORT_CONTROL_STROBE);
+#define CLEAR_STROBE parport_write_control(DPORT, data->lastcontrol);
+#define STILL_RUNNING 0x4000
+#define RETURN(value) { DRESULT=(value); \
+ data->depth--; \
+ if (data->mode==PARPORT_ATOMIC) { \
+ if (data->depth>=0) { \
+ if (!(data->pc[data->depth]&STILL_RUNNING)) \
+ data->in_function[data->depth](data) ; \
+ } else data->call_back(data); \
+ } \
+ return; }
+#define RETURN_ERROR { DPORT->cad->ieee1284.flags|=PARPORT_IEEE1284_ERROR; \
+ RETURN(-EIO); }
+#define STATE_NULL 0
+#define STATE_A 1
+#define STATE_B 2
+#define STATE_C 3
+#define STATE_D 4
+#define SET_STATE(state) data->pc[mydepth]=(state);
+#define CALL(sub_fun) { data->pc[data->depth++]|=STILL_RUNNING; \
+ data->in_function[data->depth]=NULL; sub_fun(data); \
+ data->pc[mydepth]&=(int)(~STILL_RUNNING); \
+ if (DRESULT==TRYING_AGAIN) return; }
+#define STILL_TRYING { DRESULT=TRYING_AGAIN; return; }
+#define WAIT_RESET data->wait_short=0;
+#define STATE_INI(NAME) \
+ mydepth=data->depth; \
+ if (!data->in_function[mydepth]) { \
+ data->pc[mydepth]=STATE_NULL; \
+ data->in_function[mydepth]=NAME; \
+ }
+
+/**********************************
+ *** negotiating ieee1284_modes ***
+ **********************************/
+
+/* Negotiate an IEEE 1284 mode.
+ * return values are:
+ * 0 - handshake failed; peripheral is not compliant (or none present)
+ * 1 - handshake OK; IEEE1284 peripheral present but mode not available
+ * 2 - handshake OK; IEEE1284 peripheral and mode available
+ */
+void parport_ieee1284_negotiate_p(struct parport_argument_packet *data)
+{
+/* FIXME: support ECP/EPP parports */
+ int ret, mydepth;
+ unsigned char imode;
+
+ STATE_INI(parport_ieee1284_negotiate_p);
+ imode=DDATA;
+
+ switch (data->pc[mydepth]) {
+ case STATE_NULL:
+ SET_STATE(STATE_A);
+ CALL(parport_quit_ieee1284_modes_p);
+
+ case STATE_A: parport_write_data(DPORT, imode);
+ UDELAY(5);
+ WRITE_CONTROL(PARPORT_CONTROL_AUTOFD|
+ PARPORT_CONTROL_INIT);
+ UDELAY(1);
+ SET_STATE(STATE_B);
+ WAIT_RESET;
+
+ case STATE_B:
+ if ((ret=parport_wait_status(data, PARPORT_STATUS_ERROR|
+ PARPORT_STATUS_SELECT|
+ PARPORT_STATUS_PAPEROUT|
+ PARPORT_STATUS_ACK,
+ PARPORT_STATUS_ERROR|
+ PARPORT_STATUS_SELECT|
+ PARPORT_STATUS_PAPEROUT, 4))==
+ TIMEOUT) {
+ WRITE_CONTROL(PARPORT_CONTROL_INIT|
+ PARPORT_CONTROL_SELECT);
+ RETURN(0);
+ }
+ if (ret==TRYING_AGAIN) STILL_TRYING;
+
+ SET_STROBE;
+ UDELAY(5);
+ CLEAR_STROBE;
+ UDELAY(1);
+
+ WRITE_CONTROL(PARPORT_CONTROL_INIT);
+ SET_STATE(STATE_C);
+ WAIT_RESET;
+
+ case STATE_C:
+ if ((ret=parport_wait_status(data, PARPORT_STATUS_ACK|
+ PARPORT_STATUS_PAPEROUT,
+ PARPORT_STATUS_ACK, 4))==
+ TIMEOUT) {
+ WRITE_CONTROL(PARPORT_CONTROL_INIT|
+ PARPORT_CONTROL_SELECT);
+ RETURN(1);
+ }
+ if (ret==TRYING_AGAIN) STILL_TRYING;
+
+ if (DPORT->cad) {
+ if (!(imode&~IEEE1284_REQUEST_ID))
+ imode=IEEE1284_NIBBLE_MODE_BIT;
+ else imode=(imode&~IEEE1284_REQUEST_ID);
+ DPORT->cad->ieee1284.current_mode=imode;
+ }
+ }
+ RETURN(2);
+}
+int parport_ieee1284_negotiate(struct parport *port, int mode,
+ struct parport_argument_packet *data) {
+ data->depth=0;
+ data->in_function[0]=NULL;
+ data->port=port;
+ DDATA=mode;
+ data->sum_delay=0;
+ parport_ieee1284_negotiate_p(data);
+ return DRESULT;
+}
+
+
+void parport_quit_ieee1284_modes_p(struct parport_argument_packet *data)
+{
+/* FIXME: support ECP/EPP parports here or elsewhere */
+ int ret, mydepth;
+
+ STATE_INI(parport_quit_ieee1284_modes_p);
+
+ switch (data->pc[mydepth]) {
+ case STATE_NULL:
+ WRITE_CONTROL(PARPORT_CONTROL_INIT|
+ PARPORT_CONTROL_SELECT);
+ UDELAY(1);
+ if (DPORT->cad) {
+ DPORT->cad->ieee1284.current_mode=IEEE1284_NONE;
+ DPORT->cad->ieee1284.flags&=~PARPORT_IEEE1284_ERROR;
+ }
+ SET_STATE(STATE_A);
+ WAIT_RESET;
+
+ case STATE_A:
+ if ((ret=parport_wait_status(data, PARPORT_STATUS_BUSY,
+ 0, 4))==TIMEOUT) {
+ UDELAY(500);
+ RETURN(0);
+ }
+ if (ret==TRYING_AGAIN) STILL_TRYING;
+ WRITE_CONTROL(PARPORT_CONTROL_INIT|
+ PARPORT_CONTROL_AUTOFD|
+ PARPORT_CONTROL_SELECT);
+ UDELAY(1);
+ SET_STATE(STATE_B);
+ WAIT_RESET;
+
+ case STATE_B:
+ if (parport_wait_status(data, PARPORT_STATUS_BUSY,
+ PARPORT_STATUS_BUSY, 4)==
+ TRYING_AGAIN) STILL_TRYING;
+ WRITE_CONTROL(PARPORT_CONTROL_INIT|
+ PARPORT_CONTROL_SELECT);
+ UDELAY(500);
+ RETURN(0);
+ }
+
+ return;
+}
+void parport_quit_ieee1284_modes(struct parport *port,
+ struct parport_argument_packet *data) {
+ data->depth=0;
+ data->sum_delay=0;
+ data->in_function[0]=NULL;
+ data->port=port;
+ parport_quit_ieee1284_modes_p(data);
+ return;
+}
+
+/***********************************************************************/
+
+/* The following functions will be needed by the IEEE1284-distributor */
+void write_ECPemu_p(struct parport_argument_packet*);
+void read_ECPemu_p(struct parport_argument_packet*);
+void set_ECPemu_channel_p(struct parport_argument_packet*);
+int query_ECPemu_reversedata_available(struct parport*);
+void read_nibblemode_p(struct parport_argument_packet*);
+void read_bytemode_p(struct parport_argument_packet*);
+
+/*****************************************************************************
+ *** IEEE1284-distributor - below here, these are the only functions ***
+ *** to be exported to other modules. These distributor functions will ***
+ *** call the appropriate function according to the hardware capabilities. ***
+ *****************************************************************************/
+#define HIGHEST_FORWARD_MODE(p) ((p)->cad->ieee1284.highest_forward_mode)
+#define HIGHEST_REVERSE_MODE(p) ((p)->cad->ieee1284.highest_reverse_mode)
+#define IDLE_MODE(p) ((p)->cad->ieee1284.idle_mode)
+#define NEGO_MODE(m) ( ((m)==IEEE1284_NIBBLE_MODE_BIT) ? \
+ IEEE1284_NIBBLE_MODE : (m) )
+
+#define DO_NEGO(IMODE, NAME, FORCE, NEXTSTATE) \
+ SET_STATE(NEXTSTATE); \
+ DRESULT=2; \
+ must_nego=( ((FORCE)) || \
+ ((IMODE)!=DPORT->cad->ieee1284.current_mode) || \
+ (DPORT->cad->ieee1284.flags&PARPORT_IEEE1284_ERROR) ); \
+ if (must_nego) { \
+ if ((IMODE)==IEEE1284_NONE) { \
+ CALL(parport_quit_ieee1284_modes_p); \
+ DRESULT=2; \
+ } else { \
+ DDATA=NEGO_MODE(IMODE); \
+ CALL(parport_ieee1284_negotiate_p); \
+ } \
+ } \
+ case NEXTSTATE: \
+ if (DRESULT!=2) { \
+ printk(NAME \
+ ": error while negotiating IEEE1284 mode\n"); \
+ RETURN_ERROR; \
+ }
+
+void parport_ieee1284_write_block_p(struct parport_argument_packet *data)
+{
+ int must_nego=0, mydepth;
+ unsigned char mode;
+
+ STATE_INI(parport_ieee1284_write_block_p);
+
+ switch (data->pc[mydepth]) {
+ case STATE_NULL:
+ DO_NEGO(HIGHEST_FORWARD_MODE(data->port), \
+ "parport_ieee1284_write_block", 0, STATE_A);
+
+ SET_STATE(STATE_B);
+ switch (data->port->cad->ieee1284.current_mode) {
+ case IEEE1284_NONE:
+ printk("parport_ieee1284_write_block: compatibility mode"
+ "not yet available - wait for later
Linux kernels!\n");
+ RETURN(-EIO);
+ /* FIXME: call the appropriate function in lp.c! */
+
+ case IEEE1284_ECP:
+ case IEEE1284_ECPRLE:
+ /* FIXME: support ECP parport */
+ data->res_txt="parport_ieee1284_write_block: "
+ "error while emulating ECP by software";
+ CALL(write_ECPemu_p);
+ break;
+ case IEEE1284_EPP:
+ printk("parport_ieee1284_write_block: IEEE1284 EPP mode "
+ "not yet available - wait for later
Linux kernels!\n");
+ RETURN(-EIO);
+ /* FIXME! */
+ }
+
+ case STATE_B:
+ if ((DSAVE_RESULT=DRESULT)<0) {
+ printk("%s\n", data->res_txt);
+ RETURN_ERROR;
+ }
+ SET_STATE(STATE_C);
+
+ case STATE_C:
+
+ switch (data->port->cad->flags&(PARPORT_FORWARD|PARPORT_REVERSE)) {
+ case 0: mode=IDLE_MODE(DPORT); break;
+ case PARPORT_REVERSE: mode=HIGHEST_REVERSE_MODE(data->port);
+ default: mode=0;
+ }
+ if (!mode) RETURN(DRESULT);
+
+ DO_NEGO(mode, \
+ "parport_ieee1284_write_block", 0, STATE_D);
+ }
+
+ RETURN(DSAVE_RESULT);
+}
+int parport_ieee1284_write_block(struct parport *port, char *buffer,
+ long int len,
+ struct parport_argument_packet *data) {
+ data->depth=0;
+ data->in_function[0]=NULL;
+ data->port=port;
+ data->buffer=buffer;
+ data->bufferlen=len;
+ data->sum_delay=0;
+ parport_ieee1284_write_block_p(data);
+ return DRESULT;
+}
+
+
+void parport_ieee1284_read_block_p(struct parport_argument_packet *data)
+{
+ int must_nego=0, mydepth;
+ unsigned char mode;
+
+ STATE_INI(parport_ieee1284_read_block_p);
+
+ switch (data->pc[mydepth]) {
+ case STATE_NULL:
+ DO_NEGO(HIGHEST_REVERSE_MODE(data->port), \
+ "parport_ieee1284_read_block", 0, STATE_A);
+
+ SET_STATE(STATE_B);
+ switch (data->port->cad->ieee1284.current_mode) {
+ case IEEE1284_NIBBLE_MODE_BIT:
+ data->res_txt="parport_ieee1284_read_block: "
+ "error while reading nibble mode";
+ CALL(read_nibblemode_p);
+ break;
+ case IEEE1284_BYTE_MODE:
+ data->res_txt="parport_ieee1284_read_block: "
+ "error while reading byte mode";
+ CALL(read_bytemode_p);
+ break;
+ case IEEE1284_ECP:
+ case IEEE1284_ECPRLE:
+ /* FIXME: support ECP parport */
+ data->res_txt="parport_ieee1284_read_block: "
+ "error while emulating ECP by software";
+ CALL(read_ECPemu_p);
+ break;
+ case IEEE1284_EPP:
+ printk("parport_ieee1284_write_block: IEEE1284 EPP mode "
+ "not yet available - wait for later
Linux kernels!\n");
+ RETURN(-EIO);
+ /* FIXME! */
+ }
+
+ case STATE_B:
+ if ((DSAVE_RESULT=DRESULT)<0) {
+ printk("%s\n", data->res_txt);
+ RETURN_ERROR;
+ }
+ SET_STATE(STATE_C);
+
+ case STATE_C:
+ switch (data->port->cad->flags&(PARPORT_FORWARD|PARPORT_REVERSE)) {
+ case 0: mode=IDLE_MODE(DPORT); break;
+ case PARPORT_FORWARD: mode=HIGHEST_FORWARD_MODE(data->port);
+ default: mode=0;
+ }
+ if (!mode) RETURN(DRESULT);
+
+ DO_NEGO(mode, \
+ "parport_ieee1284_read_block", 0, STATE_D);
+ }
+
+ RETURN(DSAVE_RESULT);
+}
+long parport_ieee1284_read_block(struct parport *port, char *buffer,
+ long int len,
+ struct parport_argument_packet *data) {
+ data->depth=0;
+ data->in_function[0]=NULL;
+ data->port=port;
+ data->buffer=buffer;
+ data->bufferlen=len;
+ data->sum_delay=0;
+ parport_ieee1284_read_block_p(data);
+ return DRESULT;
+}
+
+
+void parport_ieee1284_change_channel_p(struct parport_argument_packet *data)
+{
+ int mydepth;
+
+ STATE_INI(parport_ieee1284_change_channel_p);
+
+ if (DPORT->cad->ieee1284.flags&PARPORT_IEEE1284_ERROR) {
+ printk("parport_ieee1284_change_channel_p: Previous IEEE1284 "
+ "error not yet cleared\n");
+ RETURN(-EIO);
+ }
+
+ switch (data->port->cad->ieee1284.current_mode) {
+ case IEEE1284_ECP:
+/* FIXME: support ECP parports */
+ if (data->pc[mydepth]==STATE_NULL) {
+ SET_STATE(STATE_A);
+ CALL(set_ECPemu_channel_p);
+ }
+
+ if (DRESULT<0) {
+ printk("parport_ieee1284_change_channel: "
+ "error while emulating ECP by software:
%ld\n", DRESULT);
+ RETURN_ERROR;
+ }
+ RETURN(0);
+
+ case IEEE1284_EPP:
+ printk("parport_ieee1284_change_channel: IEEE1284 EPP mode"
+ "not yet available - wait for later Linux kernels!\n");
+ RETURN(-EIO); /* FIXME! */
+ default:
+ printk("parport_ieee1284_change_channel: no channel or address "
+ "defined in IEEE1284 mode 0x%02x\n",
+ DPORT->cad->ieee1284.current_mode);
+ RETURN(-EIO); /* no channel/address for other modes! */
+ }
+}
+int parport_ieee1284_change_channel(struct parport *port, char *buffer,
+ struct parport_argument_packet *data) {
+ data->depth=0;
+ data->in_function[0]=NULL;
+ data->port=port;
+ data->buffer=buffer;
+ data->sum_delay=0;
+ parport_ieee1284_change_channel_p(data);
+ return DRESULT;
+}
+
+
+int parport_ieee1284_query_state(struct parport *port)
+{
+ if (port->cad->ieee1284.flags&PARPORT_IEEE1284_ERROR) return -EIO;
+ switch (port->cad->ieee1284.current_mode) {
+ case IEEE1284_BYTE_MODE:
+ if (query_ECPemu_reversedata_available(port))
+ /* yes, it's the same signal as in ECP */
+ return IEEE1284_REVERSE_DATA_AVAILABLE;
+ return 0;
+/* FIXME: support ECP/EPP parports and EPP emu*/
+ case IEEE1284_ECP:
+ if (query_ECPemu_reversedata_available(port))
+ return IEEE1284_REVERSE_DATA_AVAILABLE;
+ return 0;
+ }
+ return 0;
+}
+
+
+unsigned int parport_ieee1284_set_modemask(struct parport *port,
+ unsigned int mask, int flags)
+{
+ /* ...IDLE_FORWARDMODE means: when ready to write or read,
+ set a forward mode; same for ...IDLE_REVERSEMODE. If both
+ are set: mode is just left as it is.
+ Default: (both bits cleared) Change to the "highest" "available"
+ mode, where the device can indicate "reverse data available". */
+
+ if (!(flags&PARPORT_IGNORE_PROBE_INFO))
+ mask&=(port->probe_info.ieee1284_modes|
+ (port->probe_info.ieee1284_modes<<8));
+
+ if (!(flags&PARPORT_IGNORE_PORT_INFO)) {
+ if (!(port->modes&PARPORT_MODE_PCPS2))
+ mask&=(0xff|((~IEEE1284_BYTE_MODE)<<8));
+ if (!(port->modes&(PARPORT_MODE_PCPS2|
+ 0 /* PARPORT_MODE_PCECP */ )))
+ mask&=(0xff|((~IEEE1284_ECPRLE)<<8));
+ if (!(port->modes&(0 /* PARPORT_MODE_PCPS2 */ |
+ 0 /* PARPORT_MODE_PCEPP */ )))
+ mask&=(0xff|((~IEEE1284_EPP)<<8));
+ /* FIXME: Distributor-functions doesn't yet support ECP/EPP parports.
+ There is no emulation by software for EPP */
+ }
+
+ HIGHEST_FORWARD_MODE(port)=mask&0xff;
+ HIGHEST_REVERSE_MODE(port)=(mask>>8)&0xff;
+
+ if ( (HIGHEST_FORWARD_MODE(port)&IEEE1284_EPP) &&
+ ( (!(flags&PARPORT_IEEE1284_PREFER_ECP)) ||
+ (!(HIGHEST_FORWARD_MODE(port)&IEEE1284_ECP)) ) )
+ HIGHEST_FORWARD_MODE(port)=IEEE1284_EPP;
+ else if (HIGHEST_FORWARD_MODE(port)&IEEE1284_ECP)
+ HIGHEST_FORWARD_MODE(port)&=IEEE1284_ECPRLE;
+ else if (HIGHEST_FORWARD_MODE(port)&IEEE1284_BYTE_MODE)
+ HIGHEST_FORWARD_MODE(port)=IEEE1284_NONE;
+ else if (HIGHEST_FORWARD_MODE(port)&IEEE1284_NIBBLE_MODE_BIT)
+ HIGHEST_FORWARD_MODE(port)=IEEE1284_NONE;
+
+ if ( (HIGHEST_REVERSE_MODE(port)&IEEE1284_EPP) &&
+ ( (!(flags&PARPORT_IEEE1284_PREFER_ECP)) ||
+ (!(HIGHEST_REVERSE_MODE(port)&IEEE1284_ECP)) ) )
+ HIGHEST_REVERSE_MODE(port)=IEEE1284_EPP;
+ else if (HIGHEST_REVERSE_MODE(port)&IEEE1284_ECP)
+ HIGHEST_REVERSE_MODE(port)&=IEEE1284_ECPRLE;
+ else if (HIGHEST_REVERSE_MODE(port)&IEEE1284_BYTE_MODE)
+ HIGHEST_REVERSE_MODE(port)=IEEE1284_BYTE_MODE;
+ else if (HIGHEST_REVERSE_MODE(port)&IEEE1284_NIBBLE_MODE_BIT)
+ HIGHEST_REVERSE_MODE(port)=IEEE1284_NIBBLE_MODE_BIT;
+
+ port->cad->ieee1284.flags=
+ ( ( port->cad->ieee1284.flags &
+ (~(PARPORT_FORWARD|PARPORT_REVERSE)) ) |
+ ( flags & (PARPORT_FORWARD|PARPORT_REVERSE) ) );
+
+ if (flags&PARPORT_FORWARD)
+ if (flags&PARPORT_REVERSE)
+ IDLE_MODE(port)=IEEE1284_NONE;
+ else IDLE_MODE(port)=HIGHEST_FORWARD_MODE(port);
+ else if (flags&PARPORT_REVERSE)
+ IDLE_MODE(port)=HIGHEST_REVERSE_MODE(port);
+ else {
+ if (HIGHEST_FORWARD_MODE(port)&
+ (IEEE1284_EPP|IEEE1284_ECP))
+ IDLE_MODE(port)=HIGHEST_FORWARD_MODE(port);
+ else if (HIGHEST_REVERSE_MODE(port)&
+ (IEEE1284_EPP|IEEE1284_ECP))
+ IDLE_MODE(port)=HIGHEST_REVERSE_MODE(port);
+ else if (HIGHEST_REVERSE_MODE(port)&IEEE1284_BYTE_MODE)
+ IDLE_MODE(port)=IEEE1284_BYTE_MODE;
+ else IDLE_MODE(port)=IEEE1284_NONE;
+ }
+
+ return HIGHEST_FORWARD_MODE(port)|(HIGHEST_REVERSE_MODE(port)<<8);
+}
+
+
+#define NIBBLE_IDLE(d) (((d)==PARPORT_REVERSE)?0:(PARPORT_CONTROL_INIT| \
+ PARPORT_CONTROL_SELECT))
+#define BYTE_IDLE(d) (((d)==PARPORT_REVERSE)?(PARPORT_CONTROL_INIT| \
+ PARPORT_CONTROL_DIRECTION): \
+ (PARPORT_CONTROL_INIT| \
+ PARPORT_CONTROL_SELECT))
+#define ECP_IDLE(d) (((d)==PARPORT_REVERSE)?(PARPORT_CONTROL_AUTOFD| \
+ PARPORT_CONTROL_DIRECTION): \
+ (PARPORT_CONTROL_INIT| \
+ PARPORT_CONTROL_AUTOFD))
+void parport_ieee1284_set_mode_p(struct parport_argument_packet *data)
+{
+ int dir, must_nego=0, mydepth;
+ unsigned char m=0, b=0;
+
+ STATE_INI(parport_ieee1284_set_mode_p);
+ switch (data->pc[mydepth]) {
+ case STATE_NULL:
+ dir=DDATA;
+ if (dir==0)
dir=(DPORT->cad->flags&(PARPORT_FORWARD|PARPORT_REVERSE));
+ /* dir == AUTO */
+
+ switch (dir) {
+ case 0: m=IDLE_MODE(DPORT); break;
+ case PARPORT_FORWARD: m=HIGHEST_FORWARD_MODE(DPORT); break;
+ case PARPORT_REVERSE: m=HIGHEST_REVERSE_MODE(DPORT); break;
+ default: if (DPORT->cad->ieee1284.current_mode==
+ HIGHEST_FORWARD_MODE(DPORT))
+ m=HIGHEST_FORWARD_MODE(DPORT);
+ else if (DPORT->cad->ieee1284.current_mode==
+ HIGHEST_REVERSE_MODE(DPORT))
+ m=HIGHEST_REVERSE_MODE(DPORT);
+ else m=IDLE_MODE(DPORT);
+ }
+
+ switch (m) {
+ case IEEE1284_NIBBLE_MODE_BIT:
+ b=NIBBLE_IDLE(PARPORT_REVERSE);
+ break;
+ case IEEE1284_BYTE_MODE:
+ b=BYTE_IDLE(PARPORT_REVERSE);
+ break;
+ case IEEE1284_ECP:
+ b=ECP_IDLE(PARPORT_FORWARD);
+ break;
+/* case IEEE1284_EPP:
+ b=EPP_IDLE(PARPORT_FORWARD);
+ break; */
+ }
+ DSAVE_RESULT=b;
+
+ DO_NEGO(m, "parport_ieee1284_set_mode", 1, STATE_A);
+ b=DSAVE_RESULT;
+ parport_write_data(DPORT, b);
+ UDELAY(1);
+ }
+
+ RETURN(0);
+}
+int parport_ieee1284_set_mode(struct parport *port, unsigned char dir,
+ struct parport_argument_packet *data) {
+ data->depth=0;
+ data->in_function[0]=NULL;
+ data->port=port;
+ DDATA=dir;
+ data->sum_delay=0;
+ parport_ieee1284_set_mode_p(data);
+ return DRESULT;
+}
+
+
-- To unsubscribe, send mail to: linux-parport-request@torque.net --
-- with the single word "unsubscribe" in the body of the message. --
This archive was generated by hypermail 2.0b3 on Wed 30 Dec 1998 - 10:18:28 EST