/* parport.c - simple parallel port driver TODO: - select devices properly depending on the node name (parport0...) - use ioperm (when available) - get it working on oskit-mach (need ioperm) - use interrupts not polling (when available) - get a replacement for those printf's - argp support - implement reading (this thing can read can't it?) - implement enhanced modes - port CUPS, so we can actualy use this crap ;) Written by Robert Millan , 2003 This is based on hurd-one.c. The hurd-one.c source says: Written by Wolfgang Jaehrling , 2001 This is based on hurd/trans/hello.c. The hello.c source says: Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc. Gordon Matzigkeit , 1999 It also uses parts of hurd/trans/null.c. The null.c source says: Copyright (C) 1995,96,97,98,99,2001 Free Software Foundation, Inc. Written by Miles Bader 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, 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 */ #define _GNU_SOURCE 1 #include #include /* I/O */ #include /* exit () */ #include /* Error numers */ #include /* O_READ etc. */ #include #include /* we need ioperm working on Mach, for now we have this ugly hack */ //define USE_IOPERM static unsigned short probe_port (unsigned short p); static void send_char (char c); static unsigned short base; #define Rdat base+0x00 #define Rsta base+0x01 #define Rcon base+0x02 /* Trivfs hooks. */ int trivfs_fstype = FSTYPE_MISC; /* Generic trivfs server */ int trivfs_fsid = 0; /* Should always be 0 on startup */ int trivfs_allow_open = O_READ | O_WRITE; /* Actual supported modes: */ int trivfs_support_read = 0; int trivfs_support_write = 1; int trivfs_support_exec = 0; /* May do nothing... */ void trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t *st) { /* .. and we do nothing */ } error_t trivfs_goaway (struct trivfs_control *cntl, int flags) { exit (EXIT_SUCCESS); } error_t trivfs_S_io_read (trivfs_protid_t cred, mach_port_t reply, mach_msg_type_name_t replyPoly, data_t *data, mach_msg_type_number_t *data_len, loff_t offset, vm_size_t amount) { /* Deny access if they have bad credentials. */ if (!cred) return EOPNOTSUPP; else if (!(cred->po->openmodes & O_READ)) return EBADF; /* Nothing to read yet */ *data_len = amount; return 0; } kern_return_t trivfs_S_io_write (trivfs_protid_t cred, mach_port_t reply, mach_msg_type_name_t replyPoly, data_t data, mach_msg_type_number_t datalen, loff_t offset, vm_size_t *amount) { if (!cred) return EOPNOTSUPP; else if (!(cred->po->openmodes & O_WRITE)) return EBADF; int i; if (probe_port (0x0378)) base = 0x0378; else if (probe_port (0x0278)) base = 0x0278; else if (probe_port (0x03bc)) base = 0x03bc; else { printf ("No printers found\n"); exit (1); } printf ("Found printer at port 0x%03x\n", base); #ifdef USE_IOPERM if (ioperm(base, 3, 1)) { printf("Could not access %03x-%03x: %s.\n", base, base+3, strerror(errno)); exit(1); } #endif /* Initialise printer: strobe = 0 autofeed = 0 initialise = 1 selected = 1 interrupt = 0 that makes it xxx0 0110b == 0x0C */ outb (0x0C, Rcon); printf ("Printer regs:\n Rdat = %d\n Rsta = %d\n Rcon = %d\n", inb (Rdat), inb (Rsta), inb (Rcon)); for (i = 0; i < datalen; i++) send_char (data[i]); *amount = datalen; return 0; } /* Tell how much data can be read from the object without blocking for a "long time" (this should be the same meaning of "long time" used by the nonblocking flag. */ kern_return_t trivfs_S_io_readable (struct trivfs_protid *cred, mach_port_t reply, mach_msg_type_name_t replytype, mach_msg_type_number_t *amount) { if (!cred) return EOPNOTSUPP; else if (!(cred->po->openmodes & O_READ)) return EINVAL; else *amount = 10000; /* Dummy value */ return 0; } /* Truncate file. */ kern_return_t trivfs_S_file_set_size (trivfs_protid_t cred, mach_port_t reply, mach_msg_type_name_t replyPoly, loff_t new_size) { if (!cred) return EOPNOTSUPP; else return 0; } /* Change current read/write offset */ error_t trivfs_S_io_seek (trivfs_protid_t cred, mach_port_t reply, mach_msg_type_name_t replyPoly, loff_t offset, int whence, loff_t *newp) { if (! cred) return EOPNOTSUPP; else return 0; } /* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG. Block until one of the indicated types of i/o can be done "quickly", and return the types that are then available. TAG is returned as passed; it is just for the convenience of the user in matching up reply messages with specific requests sent. */ kern_return_t trivfs_S_io_select (trivfs_protid_t cred, mach_port_t reply, mach_msg_type_name_t replyPoly, int *type) { if (!cred) return EOPNOTSUPP; else if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ)) || ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE))) return EBADF; else *type &= ~SELECT_URG; return 0; } /* Well, we have to define these four functions, so here we go: */ kern_return_t trivfs_S_io_get_openmodes (struct trivfs_protid *cred, mach_port_t reply, mach_msg_type_name_t replytype, int *bits) { if (!cred) return EOPNOTSUPP; else { *bits = cred->po->openmodes; return 0; } } error_t trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred, mach_port_t reply, mach_msg_type_name_t replytype, int mode) { if (!cred) return EOPNOTSUPP; else return 0; } kern_return_t trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred, mach_port_t reply, mach_msg_type_name_t replytype, int bits) { if (!cred) return EOPNOTSUPP; else return 0; } kern_return_t trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred, mach_port_t reply, mach_msg_type_name_t replytype, int bits) { if (!cred) return EOPNOTSUPP; else return 0; } int main (void) { error_t err; mach_port_t bootstrap; struct trivfs_control *fsys; task_get_bootstrap_port (mach_task_self (), &bootstrap); if (bootstrap == MACH_PORT_NULL) error (1, 0, "Must be started as a translator"); /* Reply to our parent */ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys); mach_port_deallocate (mach_task_self (), bootstrap); if (err) error (1, err, "trivfs_startup failed"); /* Launch. */ ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer, 0); return 0; } unsigned short probe_port (unsigned short p) { #ifdef USE_IOPERM if (ioperm (p, 1, 1)) { printf("Could not access port 0x%03x: %s.\n", p, strerror(errno)); exit(1); } #endif /* we probe the port by sending a value and checking it's accepted. need to do this twice to avoid accidental matches */ outb (0xFF, p); if (0xFF != inb (p)) return 0; outb (0x00, p); if (0x00 != inb (p)) return 0; return 1; } void send_char (char c) { /* wait by polling */ while ((inb (Rsta) & 0x80) == 0) {}; /* char to send */ outb (c, Rdat); /* strobe sequence */ char a = inb (Rcon); outb (a | 0x01, Rcon); outb (a & 0xFE, Rcon); }