./filtertest.c0000644000076400007640000000024410526160331011735 0ustar wdwwdw#include #include main() { int ccc; while ((ccc = getchar()) != EOF) { putchar(ccc); if (ccc == '\n') putchar('\r'); } exit(0); } ./ihcp_changes.h0000644000076400007640000002061011056625467012206 0ustar wdwwdw/* * ihcp_changes.h * changes and identification macro module for Linux 2.6.x * driver for PCI hardcopy boards * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * This code released under the GPL, and in the public domain * References to IKON left in place for compatibility and historical reasons * * Please send us code modifications and BUG reports */ #ifndef IHCP_CHANGES_H #define IHCP_CHANGES_H #ifdef MODULE_DESCRIPTION MODULE_DESCRIPTION("Tahoma Technology (formerly Ikon Corporation) Pci Hardcopy Driver - 2008.08.27 12:22"); #endif #ifdef MODULE_AUTHOR MODULE_AUTHOR("Tahoma Technology (formerly Ikon Corporation) http://www.tahomatech.com"); #endif #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif /* * TODO: * * go to cdev-style device registration, eventually go * to hotplug support * * date: 2008.08.27 files: ihcp_driver.c, ihcp_var.h * * explicitly include fs.h * * unregister_chrdev always returned 0 and as of 2.6.23 is * void so don't check return value * * as of 2.6.24 new scatterlist access functions to allow * for scatterlist (page) chaining - so add version test * and macro for setting scatterlist entries * * we're not implementing chianing - if user really wants * more than a page of scatterlist we will ask kmalloc * for it and not use chaining (let kmalloc fail if not * sufficient contig memory) * * date: 2007.06.03 files: ihcp_driver.c, ikonex.c, ikonex.h, * README.ihcp, ihcp_install-2.6 * * fix byte-order default restore after plx reset (plx doesn't * set user bit after soft reset) we will now fully set to * board's current mode, except after MASTER_CLEAR in which * case it will be set to global default mode * added (missing) byte order default to install script * * go to newer ikonex.c, remove ikonex.h * * remove dates from README.ihcp * * date: 2006.11.13 files: ihcp_var.h, ihcp_driver.c * * add butt-ugly version check code to deal with new irq * handler prototype (since kernel devs won't add flag) and * find UTS_RELEASE in utsrelease.h and put it back in debug * print * * date: 2006.10.30 files: all * * fix bonehead #ifndef wrappers on all include files that * only wrapped the #define, not the file ! * * new flags for request_irq() w/back up defines for older kernels * * rename our homemade mutexes to fix collision w/new kernel ones * * use compat and unlocked ioctl entries for mixed 32/64 systems * * drop unused pt_regs arg in int handler (per 2.6.19-rcx kernels) * w/back up define for older kernels * * protect open() from unlikely race * * remove UTS_RELEASE from debug print * * * date: 2005.10.31+ files: all * * Gather all change and date related comments and macros here. * Hopefully there will be fewer tracking errors. * * date: 2005.10.31 files: all * * change pci board id to id list to support both board types * * changes to keep up w/kernel drift: * pci_dma_sync_single --> pci_dma_sync_single_for_device * pci_find_device --> pci_get_device (and pci_put_device) * * general code clean-up and comment compaction * some macros moved from _var.h to _reg.h to try to group * hardware and software macros appropriately * * new log print macros * * convert ioctl *arg to u32 so size is the same for * 32 and 64 bit operation * * use __u32 instead of int in ioctl defines to make sure * size is 32 bits. #include to pick up type * for user apps. * * (install script) * changed MAX_BOARDS default from 1 to 4 now that we check * /proc/interrupts for actual boards atached * * put owner, group and permission seting inside node creation * loop so we can't change someone else's nodes by mistake * * date: 2005.03.20 files: ihcp_driver.c, ihcp_io.h, README.ihcp * * changed some types for x86_64 compatibility * * date: 2004.10.08 files: .c * * added owner to file_operations struct * added pci_disable_device to unload code * * date: 2004.08.27 files: .c & .h * * major modifications for 2.6 kernel * * kiobuf functionality brought inside driver to allow mapping * and pinning user buffer pages - since no longer supported * directly in kernel * * reorganized some code into helper functions * * date: 2002.08.01 files: all * * corrected use of kiobuf maplist to generate scatterlist to allow for * highmem enabled machines (up to 4G) * * added kernel copy buffer for case where machine is and kernel * are highmem, but kernel does not have new scatterlist capability * in this case, we will allocate a copy buffer at init time, and * stuff the kiobuf with its pages * * changed max_phys to orders for allocation efficiency in case of * copy buf * * moved "software" macros from ihcp_reg.h to ihcp_var.h * * got rid of MODINFOBANNER macro (using MODULE_... macros now) * * date: 2002.06.29- files: ihcp_install * * assorted typos and thinkos * * corrected bug in include dir scan * removed linux-2.4 from include scan * IHCP_ENB_HIGHMEM=YES is now the default * added missing scatterlist.page support warning * in case of compile failure * * date: 2002.05.06 files: all * * attempt to autodetect some things in ihcp_install * * add MODULE_LICENSE, MODULE_AUTHOR, and MODULE_DESCRIPTION macros * * uses IHCP_[ALLOC|FREE]_KIOVEC macros to deal with RH7.2 (updated) * quirks - selected by IHCP_RH72PLUS flag in ihcp_reg.h * * use IHCP_SG_LINK macro to do .address or .page/.offset entry * of address into scatterlist - selected by IHCP_HIGHMEM flag in ihcp_reg.h * * if IHCP_USE_DMA_MASK is defined, sets pci_dma_mask to indicate board's DMA * addressing capabilities - for thoroughness - 32 bit should be default * * add flags to enable versioning if it has been enabled in the kernel * or force versioning on, or force it off * * move conditional variable/mutex structs to _var.h, for simplified * compilation and linking * * use slab.h instead of malloc.h * * date: 2001.06.18 files: .c * * move pci_enable_device() prior to device resource accesses to try to * keep non-x86 architectures happy * requires 2.3/2.4 kernel * * date: 2000.08.30 files: .c, .h * * use readl and writel rather than direct de-reference (for non x86 * architectures) this change in ihcp_reg.h * SA_SHIRQ rather than SA_INTERRUPT to allow sharing and int code * interruption * use time_before() when delaying to avoid jiffies wrap * pci_enable_device() * * date: 2000.03.24 files: all * * incorporated kiobuf mechanism for dma from user buffers * to replace dma copy buffer used in older versions * separated int level read from board and one read from * pci_dev (so we can use the hardware level to restore * board after en eeprom reload * incorporated modified version of Kaz Kylheku's mutex stuff * for SMP * add IOPB macros to allow for possible byte lane swizzling to be * dealt with in one place * convert all IPLX accesses to PLX accesses - doing accesses via the * ihcp space may have been allowing pre-fetch from plx registers * disable address space 0 pre-fetch * make int/cstat read in intr volatile * start DMA in two steps to avoid PLX bug * pause DMA after sleep in strategy * * date: 2000.03.13 files: all * * started working w/2.3.x * new pci_dev definition required changes in the way we get * base addresses * changed file_ops struct initialization to use named fields * changed wait_queue type and added initialization * * date: 2000.11.20 files: ihcp_install * * added -x test to load script line in rc.local * * date: 1999.11.11 files: all * * change the way we do plx config re-load to avoid possible * future panic with bridge chips w/short bus timeouts * * date: 1999.10.02 files: all * * re-code for 2.2 * this version no longer compatible with earlier linux revs * * date: 1999.03.16 files: .c * * changed request_irq call to register node name rather than driver * name with interrupt - so each board will have a unique int name * (ihcp0, ihcp1, ... same as node name) * * date: 1999.01.05 files: all * * start of development - initial target Linux 2.0.35 (R.H. 5.1) */ #endif /* IHCP_CHANGES_H */ ./ihcp_driver.c0000644000076400007640000033143011056627243012063 0ustar wdwwdw/* * ihcp_driver.c * code module for Linux 2.6.x driver for PCI hardcopy boards * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * This code released under the GPL, and in the public domain * References to IKON left in place for compatibility and historical reasons * * Please send us code modifications and BUG reports */ /* * linux includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * tahoma ("ikon") includes */ #include "ihcp_changes.h" #include "ihcp_io.h" #include "ihcp_reg.h" #include "ihcp_var.h" /* * prototypes */ static int ihcp_init_module(void); static void ihcp_cleanup_module(void); static void ihcp_free_units(void); static int ihcp_open(struct inode *, struct file *); static int ihcp_close(struct inode *, struct file *); static ssize_t ihcp_write(struct file *, const char *, size_t, loff_t *); static long ihcp_new_ioctl(struct file *, u_int, u_long); static int ihcp_ioctl(struct inode *, struct file *, u_int, u_long); static irqreturn_t ihcp_intr(int, void * IHCP__PT_REGS); static int ihcp_strategy(struct ihcp_unit_t *, char *, int); static void plx_soft_reset(struct ihcp_unit_t *); static void ihcp_set_global_defaults(void); static int ihcp_init_one_board(struct ihcp_unit_t *, int); static void ihcp_set_board_defaults(struct ihcp_unit_t *, int); static int ihcp_dma_alloc(struct ihcp_unit_t *, int); static void ihcp_dma_free(struct ihcp_unit_t *, int); static int ihcp_map_regs(struct ihcp_unit_t *, int); static void ihcp_unmap_regs(struct ihcp_unit_t *, int); static int ihcp_map_or_copy_buf(struct ihcp_unit_t *, int, char *, int, int); static int ihcp_unmap_or_copy_buf(struct ihcp_unit_t *, int); static void ihcp_unmap_on_error(struct ihcp_unit_t *, int); static void ihcp_unmap_user_buf(struct ihcp_unit_t *, int); static int ihcp_map_user_buf(struct ihcp_unit_t *, int); static int ihcp_maplist_to_sg_list(struct ihcp_unit_t *, int); static int ihcp_sg_list_to_iopb_list(struct ihcp_unit_t *, int); static int ihcp_maplist_check(struct ihcp_unit_t *, int); static void ihcp_set_byte_order(struct ihcp_unit_t *); /* * mutex and conditional variable stuff - was in ikon_mutex.h (Kaz Kylheku & wdw) */ void ihcp_mutex_init(ihcp_mutex_t *); void ihcp_mutex_lock(ihcp_mutex_t *); void ihcp_mutex_unlock(ihcp_mutex_t *); void ihcp_cond_init(ihcp_cond_t *); int ihcp_cond_timed_wait_rel(ihcp_cond_t *, ihcp_mutex_t *, long); void ihcp_cond_broadcast(ihcp_cond_t *); /* * configuration variables that may be specified at module install time * see ihcp_io.h for possible values * * each internal variable is paired with one that can be modified by insmod */ static int byte_order_def = -1; static int vers_speed_def = -1; static int cent_speed_def = -1; static int mode_def = -1; static int dma_time_def = -1; static int fifo_time_def = -1; static int max_boards = -1; static int max_phys_order = -1; module_param(byte_order_def, int, 0); module_param(vers_speed_def, int, 0); module_param(cent_speed_def, int, 0); module_param(mode_def, int, 0); module_param(dma_time_def, int, 0); module_param(fifo_time_def, int, 0); module_param(max_boards, int, 0); module_param(max_phys_order, int, 0); /* * internal variables will use defaults if paired modifiable variables are still -1 after insmod * most of these variables have working versions kept in the unit structures on a per-board basis */ static int ihcp_byte_order_def; static int ihcp_vers_speed_def; static int ihcp_cent_speed_def; static int ihcp_mode_def; static int ihcp_dma_time_def; static int ihcp_fifo_time_def; static int ihcp_max_boards; static int ihcp_max_phys_order; /* * some global variables */ int ihcp_device_id_list[] = {IHCP_DEVICE_ID_LIST, 0}; /* supported board PCI IDs */ static int ihcp_boards_found; /* number of boards actually detected */ static int ihcp_boards_attached; /* number of boards successfully attached */ static int ihcp_module_initialized; /* non zero if any board attaches and module initialized successfully */ static int ihcp_device_major; /* major number dynamically assigned to this driver */ static int ihcp_max_buf_pages; /* # of pages in copy buf = 2**ihcp_max_phys_order */ static int ihcp_max_buf_bytes; /* in bytes = ihcp_max_buf_pages * PAGE_SIZE */ static int ihcp_max_sg_length; /* ihcp_max_buf_pages + 1 (allows for offset) */ /* MUST TRACK KIOBUF MAP SIZE AND COPY BUF SIZE */ static int ihcp_iopb_mem_size; /* size of mem allocated for iopb list */ /* * pointer at base of unit structure array * array, rather than linked list, to try to be a little more efficient with kernel memory */ static struct ihcp_unit_t *ihcp_unit_base_p; /* * declare dummy variables used to the left of = when we read a board * register to force the write cache to flush a register write to the bus */ static volatile u_long read_dump_0; static volatile u_long read_dump_1; /* * declare our file-ops structure * * some of these have moved since 2.0 * * use named fields as of 2.3 */ struct file_operations ihcp_fops = { owner:THIS_MODULE, write:ihcp_write, ioctl:ihcp_ioctl, IHCP_UNLOCKED_IOCTL IHCP_COMPAT_IOCTL open:ihcp_open, release:ihcp_close, }; /* * get the global parameters, kmalloc and zero the unit structure array * * register interrupts, map the working registers, get the dma iopb memory & * scatterlistlist, and copy buffer if required, register the driver and initialize * things on a per-instance basis and register the driver * * unless debug printks are enabled, init_module and cleanup_module are silent, * with the exception of a dma/copy mode anouncement and a single success or * failure message for each board instance */ static int ihcp_init_module(void) { register struct ihcp_unit_t *unit_p; int instance; int unit_array_size; u_long temp; struct pci_dev *temp_pci_dev_p; int *device_id_list_p = ihcp_device_id_list; DPRINT("entering init_module"); temp = LINUX_VERSION_CODE; DPRINT("compiled against base version: %ld.%ld.%ld uts release:\n" " %s", (temp >> 16) & 0xffff, (temp >> 8) & 0xff, temp & 0xff, UTS_RELEASE); /* * identify our dma mode/copy buffer status in msg log */ IPRINT(IHCP_DMA_MODE_MSG); ihcp_module_initialized = 0; /* * establish driver and board global defaults from parameters * includes things like iopb mem size */ ihcp_set_global_defaults(); /* * aloocate and zero space for max_boards unit structs */ unit_array_size = ihcp_max_boards * sizeof(struct ihcp_unit_t); ihcp_unit_base_p = (struct ihcp_unit_t *) kmalloc(unit_array_size, GFP_KERNEL); if (ihcp_unit_base_p == NULL) { EPRINT("can't allocate unit struct array (kmalloc error)!"); return -ENOMEM; } memset(ihcp_unit_base_p, 0, unit_array_size); DPRINT("allocated unit structure array, ihcp_unit_base_p = 0x%p,\n" " size = 0x%x", ihcp_unit_base_p, unit_array_size); /* * loop, looking for our boards - there better be at least one */ DPRINT("starting search for boards"); ihcp_boards_found = 0; ihcp_boards_attached = 0; instance =0; while(*device_id_list_p) { temp_pci_dev_p = NULL; DPRINT("probing for %x boards", *device_id_list_p); /* * loop while we keep finding boards w/this id * * make sure we don't exceed max boards and go past the end * of the unit struct array * * each get_device call decrements the previous pci dev ref count, * so bump the ref count for each found - dec ("put") at unload time * (we need pci dev to persist since we use it elsewhere) */ while((temp_pci_dev_p = pci_get_device(IKON_VENDOR_ID, *device_id_list_p, temp_pci_dev_p)) != NULL) { /* * if we are still finding boards beyond max allowed, warn, * drop the ref count on the board we just found, and exit the loop * * we just exit the inner loop - and may drop in again if more * IDs in the list - is this OK ??? */ if(instance >= ihcp_max_boards) { WPRINTI("boards found > max boards!\n" " max boards should be increased"); pci_dev_put(temp_pci_dev_p); break; } unit_p = ihcp_unit_base_p + instance; unit_p->instance = instance; /* * save pointer to this board's pci dev structure * and bump the ref count, since next loop through pci_get_device * will decrement it */ unit_p->pci_dev_p = temp_pci_dev_p; pci_dev_get(unit_p->pci_dev_p); /* * do per-board init and set attach flag and bump * attached count if successful (==0) * bump instance even if not attached since board exists * and takes space in the array */ if(!ihcp_init_one_board(unit_p, instance)) { DPRINTI("%x board attached", *device_id_list_p); unit_p->unit_attached = 1; ihcp_boards_attached++; } else { EPRINTI("ihcp_init_one_board error! board not attached!"); } ihcp_boards_found++; instance++; } /* while board found */ device_id_list_p++; } /* while *device_id_list_p */ DPRINT("no (more) boards found, ending search"); /* * if no boards have attached, give back the unit structure array and return an error * if no boards are found, do the same, but print a different message */ if (!ihcp_boards_attached) { if (!ihcp_boards_found) WPRINT("no boards found! module not initialized!"); else WPRINT("%d board(s) found but no boards attached! module not initialized!", ihcp_boards_found); kfree(ihcp_unit_base_p); return -ENODEV; } /* * register the module, and get dynamic major number assigned * **TODO** go to new style cdev-type registration */ ihcp_device_major = register_chrdev(0, "ihcp", &ihcp_fops); if (ihcp_device_major < 0) { EPRINT("register_chrdev error! module not initialized!"); /* * free all resources owned by attached boards * and give back the unit array when done */ ihcp_free_units(); kfree(ihcp_unit_base_p); return -ENODEV; } DPRINT("driver registered, major # = %d", ihcp_device_major); /* * flag module as initialized and return good status to insmod */ ihcp_module_initialized = 1; IPRINT("%d board(s) found, %d board(s) attached, module initialized", ihcp_boards_found, ihcp_boards_attached); return 0; } /* end of init_module */ /* * ihcp_free_units walks through the allocated unit structure array and frees all the * resources of any board that was successfully attached, including iopb memory, * scatterlist, user buf mapping space, copy buffer (if allocated), register mappings, * and interrupt lines and disabled device * * attached boards are fully reset before anything is freed * * it looks at all the slots, up to max_boards, not up to boards_attached, because it is * theoretically possible for pcibios errors to cause some boards to be found, but not * attach (VERY UNLIKELY) * * even if not attached, it "puts" the board to decrement the ref count * * THIS ROUTINE DOES NOT RETURN THE UNIT STRUCTURE ARRAY - THAT IS LEFT TO THE CALLING ROUTINE */ static void ihcp_free_units(void) { register struct ihcp_unit_t *unit_p; int instance; int boards_detached = 0; /* * look at each unit structure element of array * if board exists (pci_dev_p) not NULL) then check for * attached and do the appropriate stuff */ DPRINT("max boards = %d, boards found = %d, boards attached = %d", ihcp_max_boards, ihcp_boards_found, ihcp_boards_attached); for (instance = 0; instance < ihcp_max_boards; instance++) { unit_p = ihcp_unit_base_p + instance; if(unit_p->pci_dev_p) { if (unit_p->unit_attached) { DPRINTI("disabling dma and interrupts"); /* * fist disable all plx interrupt enable bits * then the ihcp masks */ PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); /* * now make sure dma channel(s) and interrupts are off * we will just force all zeros into both channel's registers */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); DPRINTI("freeing interrupt, mappings, copy buffer (if any),\n" " scatterlist, iopb memory, and disabling devcice"); free_irq(unit_p->pci_dev_int_level, (void *) unit_p); ihcp_unmap_regs(unit_p, instance); ihcp_dma_free(unit_p, instance); pci_disable_device(unit_p->pci_dev_p); unit_p->unit_attached = 0; boards_detached++; } else { DPRINTI("this instance not attached - no action taken"); } /* * dec ref count incremented w/pci_get_device - if board exists */ DPRINTI("decrementing ref count"); pci_dev_put(unit_p->pci_dev_p); } } /* end of for (instance) */ IPRINT("%d board(s) detached", boards_detached); } /* end of ihcp_free_units */ /* * When our driver is unloaded, cleanup_module cleans up and frees the resources * we allocated in init_module * unregister the driver, return resources on a per-unit basis (interrupts, * user buffer mappings, iopbs, etc) and free the unit structure array */ static void ihcp_cleanup_module(void) { DPRINT("entering cleanup_module"); if (!ihcp_module_initialized) { EPRINT("cleanup called when module not previously initialized!"); return; } /* * unregister_chrdev always returned 0, now it's void */ unregister_chrdev(ihcp_device_major, "ihcp"); DPRINT("device (driver) successfully unregistered"); /* * call ihcp_free_units to release resources * and turn off attached flag(s) * * return unit structure memory */ ihcp_free_units(); kfree(ihcp_unit_base_p); ihcp_module_initialized = 0; /* * this is as good a place as any to do phony references to the read dump varaibles * to try to absolutely guarantee that the optimizer won't remove our cache flush reads * * and to try to shut lint up */ read_dump_0 += read_dump_1; return; } /* * do various error checking, then make the board available for write * and ioctl calls * * bump the usage count so driver can't be unlaoded until close called * * **TODO** we don't protect the already open test that enforces * exclusive open. that race is unlikely, and the way our boards get * used 2 different processes will never try to open the same board * anyway - but to be complete we should probably protect w/a semaphore * or mutex (or atomic test - but that will also require an add'l state * variable - open flag is wrong sense */ static int ihcp_open(struct inode *inode_p, struct file *file_p) { int instance; register struct ihcp_unit_t *unit_p; instance = MINOR(inode_p->i_rdev); DPRINTI("entering ihcp_open"); if ((instance < 0) || (instance >= ihcp_max_boards)) { EPRINTI("instance out of range!"); return -ENODEV; } unit_p = ihcp_unit_base_p + instance; DPRINTI("unit pointer = 0x%p", unit_p); if (unit_p->unit_attached == 0) { WPRINTI("open attempted when board not attached!"); return -ENODEV; } /* * Only allow a single open */ spin_lock(&unit_p->open_lock); if (unit_p->unit_open != 0) { DPRINTI("open called when already open!"); spin_unlock(&unit_p->open_lock); return -EBUSY; } unit_p->unit_open = 1; spin_unlock(&unit_p->open_lock); /* * clear unit flags - but not board type */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS; /* * set default timeouts here * timeout parameters are stored as ticks */ unit_p->dma_time = (u_long) (HZ * unit_p->dma_time_def); unit_p->fifo_time = (u_long) (HZ * unit_p->fifo_time_def); DPRINTI("open complete"); return 0; } /* * make the board unavailable for write and ioctl calls */ static int ihcp_close(struct inode *inode_p, struct file *file_p) { int instance; register struct ihcp_unit_t *unit_p; instance = MINOR(inode_p->i_rdev); DPRINTI("entering ihcp_close"); if ((instance < 0) || (instance >= ihcp_max_boards)) { EPRINTI("instance out of range!"); return -ENODEV; } unit_p = ihcp_unit_base_p + instance; DPRINTI("unit pointer = 0x%p", unit_p); if (unit_p->unit_open == 0) { WPRINTI("close attempted when board not open!"); return -ENODEV; } unit_p->unit_open = 0; /* * we don't reset the board and device here - that might kill a job that * was in the device but not yet complete - reset in detach */ DPRINTI("close complete"); return 0; } /* * do data transfer to device - and leading and trailing bytes in p-i/o mode, if * necessary. in solaris and hpux drivers, the leading and trailing bytes were * handled in strategy - we do them in write in this driver (for some reason) * * the heavy lifting is done in the strategy routine (user dma or buffer copy * & dma) * * NOTE THAT THERE ARE LOTS OF ERROR RETURNS IN THIS CODE -- IF WE EVER NEED A * MUTEX HERE, OR ITS EQUIVALENT, THESE WILL ALL NEED TO SET AN ERROR CODE AND * GOTO ... TO AVOID LEAKING OUT THE SIDE OF THE MUTEX */ static ssize_t ihcp_write(struct file *file_p, const char *user_buf_p, size_t user_count, loff_t * off_p) { struct inode *inode_p; register struct ihcp_unit_t *unit_p; int retval; int instance; int dma_count; u_char temp_char; int strategy_retval; /* * we always say we xferred all the bytes, unless error * the plx chip can't read back the dma count anyway * * even if it could, we don't know how many bytes are still in fifos at timeout */ retval = user_count; inode_p = file_p->f_dentry->d_inode; instance = MINOR(inode_p->i_rdev); DPRINTI("entering write routine, user_buf_p = 0x%p, count = 0x%x", user_buf_p, (unsigned int)user_count); if ((instance < 0) || (instance >= ihcp_max_boards)) { EPRINTI("instance out of range!"); return -ENODEV; } unit_p = ihcp_unit_base_p + instance; if (!unit_p->unit_open) { EPRINTI("write called when unit not open!"); return -EIO; } /* * check user buffer pointer for leading bytes not quad byte aligned, * and output them, up to user_count (max) */ while (((u_long) user_buf_p & 0x3) && (user_count)) { if (copy_from_user (&temp_char, user_buf_p, sizeof(temp_char))) { EPRINTI("copy_from_user error!"); return -EINVAL; } DPRINTI("sending initial byte = 0x%x, buf_p = 0x%p, count = 0x%x", temp_char, user_buf_p, (unsigned int)user_count); /* * test for fifo full while cranking out bytes - should never happen */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("fifo full while sending leading bytes!"); return -EIO; } if (unit_p->byte_order == HIGH_BYTE_FIRST) temp_char = temp_char << 24; IHCP_PUTL(IHCP_8_BIT_DATA_OUT, temp_char); user_buf_p++; user_count--; } /* end of while */ /* * we should have an aligned user buffer pointer at this point, and 0 * or more bytes left * * clear the timeout and sig rx'd flags -- also clears "waiting for" flags * strategy will set the "waiting for dma" flag. */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS; /* * loop on dma out while user count >= 4 * use do..while loop so we go through at least once * this will cause strategy to wait for fifo <1/2 full, even if no * dma output to do.....protects against many small non-dma buffers filling * fifo * * strategy expects the virtual address of the next chunk of user buffer, * and a dma count * that is 0 mod 4 (and may be 0) */ do { /* * use the min of remaining user count and max dma buffer size * (masked to 0 mod 4) as the dma count */ dma_count = (user_count < ihcp_max_buf_bytes) ? user_count : ihcp_max_buf_bytes; dma_count &= 0xfffffffc; DPRINTI("calling strategy, user_buf_p = 0x%p, dma_count = 0x%x", user_buf_p, dma_count); strategy_retval = ihcp_strategy(unit_p, (char *)user_buf_p, dma_count); DPRINTI("ihcp_strategy returns 0x%x", strategy_retval); if (strategy_retval) { WPRINTI("ihcp_strategy returns error = %d!", strategy_retval); return strategy_retval; } user_buf_p += dma_count; user_count -= dma_count; DPRINTI("bottom of do..while, bytes remaining = 0x%x", (unsigned int)user_count); } while (user_count >= 4); /* * figure out if there are any trailing bytes, update user buf pointer * and count and send the bytes via p-i/o */ while (user_count) { if (copy_from_user (&temp_char, user_buf_p, sizeof(temp_char))) { EPRINTI("copy_from_user error!"); return -EINVAL; } DPRINTI("sending trailing byte = 0x%x, buf_p = 0x%p, count = 0x%x", temp_char, user_buf_p, (unsigned int)user_count); /* * test for fifo full while cranking out bytes - should never happen */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("fifo full while sending trailing bytes!"); return -EIO; } if (unit_p->byte_order == HIGH_BYTE_FIRST) temp_char = temp_char << 24; IHCP_PUTL(IHCP_8_BIT_DATA_OUT, temp_char); user_buf_p++; user_count--; } /* end of while */ DPRINTI("leaving write routine"); /* * retval should be the original byte count if we get to this point */ return (retval); } /* * old-style wrapper for new ioctl entry points (for older kernels) */ static int ihcp_ioctl(struct inode *inode_p, struct file *file_p, u_int cmd, u_long arg) { DPRINT("instance %d: old ioctl called, calling new ioctl", MINOR(file_p->f_dentry->d_inode->i_rdev)); return ihcp_new_ioctl(file_p, cmd, arg); } /* * provide mechanism for controlling device and driver * * THIS CODE USES A FEW ERROR RETURNS - IF WE SOMEDAY NEED A MUTEX * OR EQUIVALENT, GO BACK TO SOLARIS STYLE .. SET RETVAL ERROR CODES AND * JUMP TO IOCTLEXIT, SO WE DON'T RISK LEAKING OUT THE SIDE OF A MUTEX! * * note that the ioctl routine treats arg as a pointer - to u32 in this version */ static long ihcp_new_ioctl(struct file *file_p, u_int cmd, u_long arg) { struct inode *inode_p; register struct ihcp_unit_t *unit_p; u32 return_array[IHCP_RETURN_ARRAY_SIZE]; u8 data_array[256]; struct lpregs lp_regs; u32 bits; u8 ct; u16 st; u32 lt; int count; int i, t; int instance; int command; int wait_return; inode_p = file_p->f_dentry->d_inode; instance = MINOR(inode_p->i_rdev); DPRINTI("entering new ioctl routine, command = 0x%x, arg = 0x%lx", cmd, arg); if ((instance < 0) || (instance >= ihcp_max_boards)) { EPRINTI("instance out of range!"); return -ENODEV; } unit_p = ihcp_unit_base_p + instance; if (!unit_p->unit_open) { EPRINTI("ioctl called when unit not open!"); return -EIO; } /* * get command and argument size */ command = cmd & IHCPIO_CMD_MASK; count = (cmd & IHCPIO_COUNT_MASK) >> _IOC_SIZESHIFT; DPRINTI("masked command = 0x%x, argument size = 0x%x", command, count); /* * get first word of argument (even if we are eventually doing a * copyout operation) - this saves some typeing */ if (copy_from_user(&bits, (caddr_t) arg, sizeof(bits))) { EPRINTI("copy_from_user error!"); return -EINVAL; } DPRINTI("arg = 0x%lx, *arg = 0x%x", arg, bits); /* * define mask used to strip the count and upper control bits from cases */ #define MASK IHCPIO_CMD_MASK switch (command) { case MASK & IHCPIO_DEV_RESET: /* send ~ 1us reset pulse to attached device */ DPRINTI("at DEV_RESET"); /* * set and reset device clear bit, with a delay so the pulse is long enough * for even slow devices * * udelay will make the cpu delay, but doesn't guarantee that the writes won't * stack in a write buffer * * so we will do a read before the wait to force the buffer to flush */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE)); read_dump_0 = IHCP_GETL(IHCP_MODE); udelay(1); IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE)); break; case MASK & IHCPIO_SET_CONFIG: /* set speed and if to use busy */ DPRINTI("at SET_CONFIG"); /* * mask to pertinent bits only * the calling program will use the old sbus speed definitions * we will have to convert them to the pci card bits here */ bits &= (IHCPIO_V_BURST | IHCPIO_BUSY_HANDSHAKE | IHCPIO_4_EDGE | IHCPIO_IGNORE_BUSY | IHCPIO_SPEED3); /* * if request is a centronics function and board is strapped for * versatec - error! */ if ( ((bits & (IHCPIO_BUSY_HANDSHAKE | IHCPIO_4_EDGE | IHCPIO_IGNORE_BUSY)) != 0) && ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT)) { WPRINTI("SET_CONFIG - attempt to set centronics mode on board strapped for versatec!"); return -ENOTTY; } /* * if request is a versatec only function and board is strapped for centronics - error! */ if (((bits & IHCPIO_V_BURST) != 0) && ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT)) { WPRINTI("at SET_CONFIG - attempt to set versatec mode on baord straped for centronics!"); return -ENOTTY; } /* * shuffle bits to match new board */ t = SPEED_MASK & (3 - ((bits & IHCPIO_SPEED3) >> 2)); if (bits & IHCPIO_BUSY_HANDSHAKE) t |= BUSY_NOT_ACK; if (bits & IHCPIO_4_EDGE) t |= FOUR_EDGE_HANDSHAKE; if (bits & IHCPIO_IGNORE_BUSY) t |= IGNORE_BUSY; if (bits & IHCPIO_V_BURST) t |= V_BURST; /* * plug into mode register - being careful not to disturb other bits */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & (BYTE_SWIZZLE | REVERSE_DATA_ENB)) | t); break; case MASK & IHCPIO_SET_DMATIME: /* set dma timeout parameter */ DPRINTI("at SET_DMATIME"); if (bits < DMA_TIME_MIN) bits = DMA_TIME_MIN; /* not less than min */ if (bits > DMA_TIME_MAX) bits = DMA_TIME_MAX; /* or more than max */ unit_p->dma_time = (u_long) (bits * HZ); /* set ticks */ break; case MASK & IHCPIO_SET_FIFOTIME: /* set timeout for fifo <1/2 full */ /* or fifo empty & dev rdy wait */ DPRINTI("at SET_FIFOTIME"); if (bits < FIFO_TIME_MIN) bits = FIFO_TIME_MIN; /* not less than */ if (bits > FIFO_TIME_MAX) bits = FIFO_TIME_MAX; /* or more than.. */ unit_p->fifo_time = (u_long) (bits * HZ); /* ticks */ break; case MASK & IHCPIO_GET_REGS: /* get all the board's registers */ DPRINTI("at GET_REGS"); return_array[0] = unit_p->dev_and_vendor_id; return_array[1] = unit_p->revision_id; return_array[2] = PLX_GETL(PLX_INT_CSTAT); return_array[3] = PLX_GETL(PLX_EEPROM_USER); return_array[4] = PLX_GETL(PLX_DMA_MODE_0); return_array[5] = PLX_GETL(PLX_DMA_PCI_ADD_0); return_array[6] = PLX_GETL(PLX_DMA_LOC_ADD_0); return_array[7] = PLX_GETL(PLX_DMA_COUNT_0); return_array[8] = PLX_GETL(PLX_DMA_DESC_PTR_0); return_array[9] = PLX_GETL(PLX_DMA_CMD_STAT_BOTH); return_array[10] = IHCP_GETL(IHCP_INTERRUPT_MASK); return_array[11] = IHCP_GETL(IHCP_MODE); return_array[12] = IHCP_GETL(IHCP_DEVICE_CONTROL); return_array[13] = IHCP_GETL(IHCP_INTERFACE_CONTROL); return_array[14] = IHCP_GETL(IHCP_INTERFACE_STATUS); return_array[15] = IHCP_GETL(IHCP_DEVICE_STATUS); return_array[16] = IHCP_GETL(IHCP_REVERSE_DATA); return_array[17] = IHCP_GETL(IHCP_AUTO_LTR_LOW); return_array[18] = IHCP_GETL(IHCP_AUTO_LTR_HIGH); if (copy_to_user((caddr_t) arg, return_array, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_GET_STATUS: /* get formatted version of status */ DPRINTI("at GET_STATUS"); /* * get device status bits */ ct = IHCP_GETL(IHCP_DEVICE_STATUS) & (FAULT | ONLINE | PAPER_OUT | CENTRONICS_BUSY | VERSATEC_READY); /* * shuffle status bits to match sbus format */ bits = 0; if (ct & FAULT) bits |= IHCPIO_DEV_FAULT; if (ct & ONLINE) bits |= IHCPIO_DEV_SEL; if (ct & PAPER_OUT) bits |= IHCPIO_DEV_POUT; if (ct & CENTRONICS_BUSY) bits |= IHCPIO_DEV_BUSY; if (ct & VERSATEC_READY) bits |= IHCPIO_DEV_RDY; if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_GET_BOARD: /* get board type */ DPRINTI("at GET_BOARD"); bits = IHCP_GETL(IHCP_DEVICE_STATUS) & INTERFACE_STRAP_MASK; if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_SET_VMODE: /* set versatec mode - both leave rdy ff on on pci card */ case MASK & IHCPIO_SET_VMODEX: DPRINTI("at SET_VMODE"); /* * make sure the board is strapped for versatec */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at SET_VMODE - attempt to do versatec mode set on baord strapped for centronics!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room * we could wait for room with an interrupt, but the way the board is to * be used, there should always be room */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at SET_VMODE - no room in fifo for versatec mode set!"); return -EIO; } /* * bottom two bits contain print/plot/spp control - same for sbus and pci * command byte will be in low byte unless byte swizzling selected */ lt = SET_VERSATEC_MODE | (bits & 0x03); if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* * save mode in unit structure */ unit_p->mode = bits & 0x03; break; case MASK & IHCPIO_V_CMD: /* issue versatec pulse command */ DPRINTI("at V_CMD"); /* * make sure the board is strapped for versatec - can't do this w/centr */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at V_CMD - attempt to issue versatec command to board strapped for centronics!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at V_CMD - no room in fifo for versatec command!"); return -EIO; } /* * bottom two bits contain command - we have to shuffle them * for compatibility with sbus driver */ if (bits == IHCPIO_VLTR) lt = VERSATEC_LINE_TERM; if (bits == IHCPIO_VEOT) lt = VERSATEC_EOT; if (bits == IHCPIO_VFED) lt = VERSATEC_FORM_FEED; if (bits == IHCPIO_VCLR) lt = VERSATEC_CLEAR; DPRINTI("at V_CMD, command sent = 0x%x", lt); if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); break; case MASK & IHCPIO_DATA_OUT: /* output chars from arg - # of chars in bottom of cmd */ DPRINTI("at DATA_OUT, byte count = 0x%x", count); /* * make sure # of characterss is <=255 * that is the max # of characters in arg array - for historical reasons * * we will check for room in fifo before each byte but will not wait on * an interrupt if the fifo is full - we will flag an error and return * * each byte will be sent by writing to the 8 bit data out register * this is much less effecient than writing quad bytes to the 32 bit * data out register, or to the burst out range * * it is not the intent of this driver to use this ioctl for large data * blocks, or frequent data transfers - those funtions should be served via * the write call * * this ioctl should be used for the output of a few bytes - perhaps as a * header to a write call, and should be used between write calls, or waits for * fifo less than half full * * if this ioctl is used repeatedly, without interspersed dma transfers, which * leave the fifo approximately half full, or without wait for half full ioctls * the fifo may eventually overflow * * it would be possible to incorporate a wait for less than half full interrupt * in the data out loop, but that would be overkill, considering the intended * use of this ioctl */ if (count > 255) { WPRINTI("at DATA_OUT - too many bytes!"); return -EINVAL; } if (copy_from_user(data_array, (caddr_t) arg, count)) { WPRINTI("copy_from_user error!"); return -EINVAL; } for (i = 0; i < count; i++) { if ( (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at DATA_OUT - fifo full! 0x%x bytes requested,\n" " 0x%x bytes transferred", count, i); return -EIO; } lt = data_array[i]; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_8_BIT_DATA_OUT, lt); } break; case MASK & IHCPIO_RDY_WAIT: /* wait for rdy & fifo empty - or timeout */ DPRINTI("at RDY_WAIT, wait time = %d (jiffies)", unit_p->fifo_time); /* * if already ready and fifo empty, just return * first clear any stale flags */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS; if (IHCP_GETL(IHCP_INTERFACE_STATUS) & DEVICE_AND_INT_READY) return 0; /* * make sure no left over interrupt flag */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * get the mutex, set sleeping so we can detect an abnormal * return, and indicate why we wait */ ihcp_mutex_lock(&unit_p->mutex); unit_p->sleeping = 1; unit_p->unit_flags |= IHCP_RDY_WAIT; /* * enable plx and ihcp interrupts for device and interface ready * we don't or into ihcp or plx mask reg since we know exactly * which ints to enb */ IHCP_PUTL(IHCP_INTERRUPT_MASK, DEV_AND_INT_RDY_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, (PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE)); /* * snooze until ready interrupt, timeout, or signal */ DPRINTI("at RDY_WAIT, time_now = %ld, unit_p->fifo_time = %d", jiffies, unit_p->fifo_time); wait_return = ihcp_cond_timed_wait_rel(&unit_p->cond, &unit_p->mutex, unit_p->fifo_time); /* * make sure interrupt enables are off * and clear interrupt flag in case we got here via timeout just * as interrupt set the flag */ IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); ihcp_mutex_unlock(&unit_p->mutex); /* * find out why we woke up (we could probably dump if sleeping * and use the return value of cond_timed_wait_rel directly, but ... */ if (unit_p->sleeping) { unit_p->sleeping = 0; DPRINTI("at RDY_WAIT, abnormal return from ihcp_cond_timed_wait_rel()!"); if (wait_return == IHCP_COND_WAIT_TIMEOUT) { /* * timeout may not be an error, so no error message just flag and return * caller can test for timeout on error return */ DPRINTI("at RDY_WAIT, timeout while waiting for interrupt!"); unit_p->unit_flags |= IHCP_RDY_TIMEOUT; return -EIO; } if (wait_return == IHCP_COND_WAIT_SIGNAL) { /* * flag signal received and print error message */ WPRINTI("at RDY_WAIT, signal received while waiting for interrupt"); unit_p->unit_flags |= IHCP_SIG_RECEIVED; return -EINTR; } } /* end of if sleeping */ return 0; break; case MASK & IHCPIO_HALF_WAIT: /* wait for fifo fifo_time); /* * if already less than half full, just return * first clear any stale flags */ unit_p->unit_flags &= IHCP_CLEAR_FLAGS; if (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_HALF_FULL) return 0; /* * clear any left over interrupt flag */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * get the mutex, set sleeping so we can detect an abnormal * return, and indicate why we wait */ ihcp_mutex_lock(&unit_p->mutex); unit_p->sleeping = 1; unit_p->unit_flags |= IHCP_HALF_WAIT; /* * enable plx and ihcp interrupt for fifo less than half full * we don't or into ihcp or plx mask reg since we know exactly which ints to enb */ IHCP_PUTL(IHCP_INTERRUPT_MASK, FIFO_NOT_HALF_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, (PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE)); /* * snooze until ready interrupt, timeout, or signal */ DPRINTI("at HALF_WAIT, time_now = %ld, unit_p->fifo_time = %d", jiffies, unit_p->fifo_time); wait_return = ihcp_cond_timed_wait_rel(&unit_p->cond, &unit_p->mutex, unit_p->fifo_time); /* * make sure interrupt enables are off and clear flag in case we * got here via timeout just as interrupt happened */ IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); ihcp_mutex_unlock(&unit_p->mutex); /* * find out why we woke up (we could probably dump if sleeping and * use the return value of cond_timed_wait_rel directly, but ... */ if (unit_p->sleeping) { unit_p->sleeping = 0; DPRINTI("at HALF_WAIT, abnormal return from ihcp_cond_timed_wait_rel()!"); if (wait_return == IHCP_COND_WAIT_TIMEOUT) { /* * timeout may not be an error, so no error message just flag and return * caller can test for timeout on error return */ DPRINTI("at HALF_WAIT, timeout while waiting for interrupt!"); unit_p->unit_flags |= IHCP_HALF_TIMEOUT; return -EIO; } if (wait_return == IHCP_COND_WAIT_SIGNAL) { /* * flag signal received and print error message */ WPRINTI("at HALF_WAIT, signal received while waiting for interrupt"); unit_p->unit_flags |= IHCP_SIG_RECEIVED; return -EINTR; } } /* end of if sleeping */ return 0; break; case MASK & IHCPIO_STREAM_ON: /* set streaming mode if 10106 */ DPRINTI("at STREAM_ON"); if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at STREAM_ON - attempt to set streaming mode on board strapped for versatec!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at STREAM_ON - no room in fifo for stream on command!"); return -EIO; } lt = SET_IO_MODE | DATA_STREAMING_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* * save streaming mode for compatibility with old vme ioctls */ unit_p->data_stream_mode = DATA_STREAMING_ON; break; case MASK & IHCPIO_STREAM_OFF: /* turn streaming off (if 10106) */ DPRINTI("at STREAM_OFF"); if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at STREAM_OFF - attempt to clear streaming mode on board strapped for versatec!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at STREAM_OFF - no room in fifo for stream off command!"); return -EIO; } /* * set_io_mode w/ nothing else or'd in turns off streaming * and auto ltr - we won't try to do streaming and auto ltr * at same time in this driver */ lt = SET_IO_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* * save streaming mode for compatibility with old vme ioctls */ unit_p->data_stream_mode = DATA_STREAMING_OFF; break; case MASK & IHCPIO_GET_FLAGS: /* get unit flag longword */ DPRINTI("at GET_FLAGS"); if (copy_to_user ((caddr_t) arg, &unit_p->unit_flags, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_GET_FIFO: /* get formatted fifo flags */ DPRINTI("at GET_FIFO"); /* * get fifo status bits and swizzle and complement * to match sbus driver format */ lt = IHCP_GETL(IHCP_INTERFACE_STATUS); bits = 0; if (!(lt & FIFO_NOT_FULL)) bits |= IHCPIO_FIFO_FULL; if (!(lt & FIFO_NOT_HALF_FULL)) bits |= IHCPIO_FIFO_HALF; if (lt & FIFO_EMPTY) bits |= IHCPIO_FIFO_EMPTY; if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_MASTER_CLEAR: /* master clear board - not for use by wimps! */ /* clear ihcp logic and restore all defaults */ DPRINTI("at MASTER_CLEAR"); IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * make absolutely sure that dma and interrupts are off in plx chip */ PLX_PUTL(PLX_INT_CSTAT, 0); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); /* * set and clear mclr bit - other bits in this register are also * treated like pulses, and do not persist, so we don't need to * or in a bit, then and it out */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * set and clear the device reset bit, with a delay and write * buffer flush to guarantee the delay * * the mode read between puts should force the write buffer to flush */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE)); read_dump_0 = IHCP_GETL(IHCP_MODE); udelay(1); IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE)); /* * set to current print mode, default handshake speed, and default * byte ordering * * we don't have to check for fifo space, since we just cleared it * * print mode is set only if the board is strapped for versatec * * set byte order to global default - NOT current board mode */ unit_p->byte_order = ihcp_byte_order_def; ihcp_set_byte_order(unit_p); if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { /* * versatec - set speed and print/plot mode */ IHCP_PUTL(IHCP_MODE, ((IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->vers_speed_def)); lt = SET_VERSATEC_MODE | unit_p->mode_def; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); unit_p->mode = unit_p->mode_def; } else { /* * centronics - set speed only */ IHCP_PUTL(IHCP_MODE, ((IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->cent_speed_def)); /* * clear saved data streaming mode */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } break; case MASK & IHCPIO_SOFT_ACK: /* software ack - looks like an ack from the dev */ DPRINTI("at SOFT_ACK"); /* * set and clear soft ack bit, as in mclr */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, SOFTWARE_ACK); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); break; case MASK & IHCPIO_AUTO_LTR_COUNT: /* set byte count for auto ltr function */ DPRINTI("at AUTO_LTR_COUNT, byte count = %d", bits); if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at AUTO_LTR_COUNT - attempt to set auto ltr count on board strapped for centronics!"); return -ENOTTY; } /* * auto ltr count is 16 bit register, accessed as two * separate bytes * * the count register takes count minus 1 */ IHCP_PUTL(IHCP_AUTO_LTR_LOW, (bits - 1) & 0xFF); IHCP_PUTL(IHCP_AUTO_LTR_HIGH, ((bits - 1) >> 8) & 0xFF); break; case MASK & IHCPIO_AUTO_LTR_ON: /* enable auto ltr operation */ DPRINTI("at AUTO_LTR_ON"); if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at AUTO_LTR_ON - attempt to set auto ltr mode on board strapped for centronics!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at AUTO_LTR_ON - no room in fifo for auto ltr on command!"); return -EIO; } lt = SET_IO_MODE | AUTO_LTR_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); break; case MASK & IHCPIO_AUTO_LTR_OFF: /* disable auto ltr operation */ DPRINTI("at AUTO_LTR_OFF"); if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at AUTO_LTR_OFF - attempt to clear auto ltr mode on board strapped for centronics!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ((IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at AUTO_LTR_OFF - no room in fifo for auto ltr off command!"); return -EIO; } /* * set io mode with no other bits clears auto ltr and data streaming */ lt = SET_IO_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); break; case MASK & IHCPIO_DEV_AND_VEND_ID: /* return vendor id in low 16 bits, */ /* device id in high 16 bits */ DPRINTI("at DEV_AND_VEND_ID"); bits = unit_p->dev_and_vendor_id; if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_REVISION_ID: /* return board revision id */ DPRINTI("at REVISION_ID"); bits = unit_p->revision_id; if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_LITTLE_ENDIAN: /* data will go to plotter low byte first */ DPRINTI("at LITTLE_ENDIAN"); /* * low first byte ordering (the hardware default) is set by turning off * the byte swizzle bit in the ihcp logic, and TURNING ON * the user output bit in the plx logic] */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & ~BYTE_SWIZZLE)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) | USER_OUTPUT)); unit_p->byte_order = LOW_BYTE_FIRST; break; case MASK & IHCPIO_BIG_ENDIAN: /* data will go to plotter high byte first */ DPRINTI("at BIG_ENDIAN"); /* * turn on the ihcp byte swizzle bit and TURN OFF the plx * user output bit */ IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) | BYTE_SWIZZLE)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) & ~USER_OUTPUT)); unit_p->byte_order = HIGH_BYTE_FIRST; break; case MASK & IHCPIO_DIRECT_MODE: /* direct user write of mode register - use carefully! */ DPRINTI("at DIRECT_MODE"); IHCP_PUTL(IHCP_MODE, bits); break; case MASK & IHCPIO_DEVICE_CONTROL: /* direct write to device control register */ DPRINTI("at DEVICE_CONTROL"); IHCP_PUTL(IHCP_DEVICE_CONTROL, bits); break; case MASK & IHCPIO_INTERFACE_STATUS: /* direct read of interface status register */ DPRINTI("at INTERFACE_STATUS"); bits = IHCP_GETL(IHCP_INTERFACE_STATUS); if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_DEVICE_STATUS: /* direct read of device status register */ DPRINTI("at DEVICE_STATUS"); bits = IHCP_GETL(IHCP_DEVICE_STATUS); if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & IHCPIO_REVERSE_DATA: /* direct read of reverse data register - requires */ /* fiddling of tri-state bit, and others to actually */ /* do anything useful with this */ DPRINTI("at REVERSE_DATA"); bits = IHCP_GETL(IHCP_REVERSE_DATA); if (copy_to_user((caddr_t) arg, &bits, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; /* * ioctls for compatibility with versatec and sun drivers for VME and Sbus boards */ case MASK & LPSETVERSATEC: /* compatibility - set versatec port */ DPRINTI("at LPSETVERSATEC"); /* * make sure the board is a versatec type - can't do this w/centr */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at LPSETVERSATEC - attempt to select versatec port onn board strapped for centronics!"); return -ENOTTY; } break; case MASK & LPSETCENTRONICS: /* compatibility - set centr port */ DPRINTI("at LPSETCENTRONICS"); if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at LPSETCENTRONICS - attempt to select centronics port on board strapped for versatec!"); return -ENOTTY; } break; case MASK & LPCOMMAND: /* compatibility - old style cmds */ DPRINTI("at LPCOMMAND"); /* * as much as possible, allow multiple command bits in the argument. * this is probably not the way it was intended, but we want to * protect ourselves against non-reasonable uses of the old calls */ /* * assert long reset signal */ if (bits & LPC_LRST) IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE); /* * de-assert long reset */ if (bits & LPC_DRST) IHCP_PUTL(IHCP_INTERFACE_CONTROL, IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE);; /* * test for option port request - better be strapped for centronics!! */ if (bits & LPC_SOPT) if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to select centronics port on board strapped for versatec!"); return -ENOTTY; } /* * test for versatec port selection */ if (bits & LPC_SVPT) if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to select versatec port on board strapped for centronics!"); return -ENOTTY; } /* * enable data streaming - if board is strapped for centronics! */ if (bits & LPC_DSTR) { if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to set streaming mode on board strapped for versatec!"); return -ENOTTY; } /* * this command talks to the fifo - make sure there is room !! */ if ( (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at LPCOMMAND - no room in fifo for stream on command!"); return -EIO; } lt = SET_IO_MODE | DATA_STREAMING_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* * save for posterity */ unit_p->data_stream_mode = DATA_STREAMING_ON; } /* reset data streaming bit */ if (bits & LPC_DDST) { if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to clear streaming mode on non-centronics unit!"); return -ENOTTY; } if ( (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at LPCOMMAND - no room in fifo for stream off command!"); return -EIO; } /* * set_io_mode w/ nothing else or'd in turns off streaming * and auto ltr - we won't try to do streaming and auto ltr * at same time in this driver */ lt = SET_IO_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); /* * save streaming mode for later getregs call */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } /* * test for versatec print/plot mode changes - make sure we are * a versatec board !! */ if (bits & LPC_PRINTMASK) { if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to do versatec mode set on board strapped for centronics!"); return -ENOTTY; } /* * this cmd talks to the fifo - make sure there is room !! */ if ( (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at LPCOMMAND - no room in fifo for versatec mode set!"); return -EIO; } /* * get current mode setting */ lt = unit_p->mode; /* now look at individual command bits and twiddle mode */ if (bits & LPC_VSPP) lt |= SPP_MODE; /* set spp mode */ if (bits & LPC_DSPP) lt &= ~SPP_MODE; /* clear spp */ if (bits & LPC_VPLT) lt |= PLOT_MODE; /* set plot mode */ if (bits & LPC_DVPT) lt &= ~PLOT_MODE; /* clear plot */ if (bits & LPC_PRNT) lt = NORM_PRINT_MODE; /* 0 = normal print mode */ /* * save final mode - without mode set command bit - * in unit structure */ unit_p->mode = lt; /* * write final mode to fifo - remember to write to high * byte if big-endian data mode selected */ lt |= SET_VERSATEC_MODE; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); } /* * check for software ack pulse request */ if (bits & LPC_SACK) { /* * set and clear soft ack bit, as in mclr */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, SOFTWARE_ACK); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); } /* * check for master clear request - the old vme board also issued a reset * to the device when mclr was pulsed, so we will do that also * * we will also restore the default print/plot mode (if versatec), * the device handshake speed, and the byte ordering */ if (bits & LPC_MCLR) { IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * make absolutely sure that dma and interrupts are off in plx chip */ PLX_PUTL(PLX_INT_CSTAT, 0); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); /* * set and clear mclr bit - other bits in this register are also * treated like pulses, and do not persist, so we don't need to * or in a bit, then and it out */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * set and clear the device reset bit, with a delay and write * buffer flush to guarantee the delay * * the mode read between puts should force the write buffer to flush */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) | RESET_DEVICE)); read_dump_0 = IHCP_GETL(IHCP_MODE); udelay(1); IHCP_PUTL(IHCP_INTERFACE_CONTROL, (IHCP_GETL(IHCP_INTERFACE_CONTROL) & ~RESET_DEVICE)); /* * set to current print mode, default handshake speed, and default * byte ordering * * we don't have to check for fifo space, since we just cleared it * * print mode is set only if the board is strapped for versatec * */ ihcp_set_byte_order(unit_p); if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { /* * versatec - set speed and print/plot mode */ IHCP_PUTL(IHCP_MODE, ((IHCP_GETL (IHCP_MODE) & ~SPEED_MASK) | unit_p->vers_speed_def)); lt = SET_VERSATEC_MODE | unit_p->mode_def; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); unit_p->mode = unit_p->mode_def; } else { /* * centronics - set speed only */ IHCP_PUTL(IHCP_MODE, ((IHCP_GETL (IHCP_MODE) & ~SPEED_MASK) | unit_p->cent_speed_def)); } /* * clear saved data streaming mode */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } /* * look for pulsed command request that has to go through fifo */ if (bits & (LPC_VCLR | LPC_VTFF | LPC_VEOT | LPC_VLTR)) { /* make sure the board is a versatec type - can't do w/centr */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { WPRINTI("at LPCOMMAND - attempt to do versatec pulsed command to board strapped for centronics!"); return -ENOTTY; } /* * this cmd talks to the fifo - make sure there is room !! * WE WILL ASSUME THAT THERE WILL ONLY BE ONE PULSE PER CALL */ if ( (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_FULL) == 0) { WPRINTI("at LPCOMMAND - no room in fifo for versatec pulsed command!"); return -EIO; } /* * shuffle vme style commands to match pci board */ if (bits & LPC_VLTR) lt = VERSATEC_LINE_TERM; if (bits & LPC_VEOT) lt = VERSATEC_EOT; if (bits & LPC_VTFF) lt = VERSATEC_FORM_FEED; if (bits & LPC_VCLR) lt = VERSATEC_CLEAR; if (unit_p->byte_order == HIGH_BYTE_FIRST) lt = lt << 24; IHCP_PUTL(IHCP_COMMAND_OUT, lt); } break; case MASK & LPGETREGS: /* old style (VMEbus) get regs command */ DPRINTI("at LPGETREGS"); /* * start work on interface status register */ st = 0; /* * get interface status register * and fiddle interface and device ready bits */ ct = (u_char) IHCP_GETL(IHCP_INTERFACE_STATUS); if (ct & DEVICE_AND_INT_READY) st |= OLD_DIRY; if (ct & DEVICE_READY) st |= OLD_DVRY; /* * get remembered data streaming mode */ if (unit_p->data_stream_mode == DATA_STREAMING_ON) st |= OLD_DSTR; /* * get board type from unit struct * if centronics, flag as old style option port */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) st |= OLD_SOPT; /* * save result in cis portion of lp_regs structure * * we used to put it in the top of a u_long, and the cds portion * in the bottom, but we get into trouble with "endian-ness" * moving across the various platforms * * starting with this port, we will use the struct instead */ lp_regs.cisreg = st; /* * do the same ugly things with the device status register -- * it has two halves: versatec and centronics -- only * work on the appropriate half */ st = 0; if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) { /* * work on centronics part * get the device status register and * shuffle bit positions and polarities */ ct = (u_char) IHCP_GETL(IHCP_DEVICE_STATUS); if (ct & VERSATEC_READY) st |= OLD_OACK; if (!(ct & CENTRONICS_BUSY)) st |= OLD_ONBY; if (!(ct & PAPER_OUT)) st |= OLD_OPPR; if (!(ct & ONLINE)) st |= OLD_ONSL; if (ct & FAULT) st |= OLD_OFLT; /* * look for device reset latch */ if (IHCP_GETL(IHCP_INTERFACE_CONTROL) & RESET_DEVICE) st |= OLD_OLRS; /* long reset */ } else { /* * work on versatec part */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_VERS_TTL) st |= OLD_VTTL; /* * get device status and twiddle bits */ ct = (u_char) IHCP_GETL(IHCP_DEVICE_STATUS); if (ct & VERSATEC_READY) st |= OLD_VRDY; /* vers ready */ if (ct & PAPER_OUT) st |= OLD_VPPR; /* paper ok */ if (ct & ONLINE) st |= OLD_VONL; /* vers onlin */ /* * get current mode from unit structure */ ct = unit_p->mode; if (ct & SPP_MODE) st |= OLD_VSPP; /* spp mode */ if (ct & PLOT_MODE) st |= OLD_VPLT; /* plot mode */ } /* * save in cds portion of status structure */ lp_regs.cdsreg = st; if (copy_to_user((caddr_t) arg, &lp_regs, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; case MASK & LPSETTIMVAL: /* set timeout value - old style - for dma and fifo empty */ DPRINTI("at LPSETTIMVAL"); /* * old style timeout call used fiftieths of a second as argument */ if (bits < DMA_TIME_MIN * 50) bits = DMA_TIME_MIN * 50; if (bits > DMA_TIME_MAX * 50) bits = DMA_TIME_MAX * 50; unit_p->fifo_time = (u_long) ((bits * HZ) / 50); unit_p->dma_time = (u_long) ((bits * HZ) / 50); break; case MASK & LPGETTIMVAL: /* get current timeout (in ticks ?) */ DPRINTI("at LPGETTIMVAL"); /* * return # of fiftieths of a second */ lt = (unit_p->dma_time / HZ) * 50; if (copy_to_user((caddr_t) arg, <, count)) { EPRINTI("copy_to_user error!"); return -EINVAL; } break; default: /* * we don't print an error message here * if we are involved in a pipe, the pipe logic may send * us a generic ioctl to determine if we are a terminal * we have to return an error or we will give a false * terminal indication. */ return -EINVAL; break; } return 0; } /* * our interrupt handler - registered with the system in init_module * each board's instance will register the same handler, but with a different unit * structure pointer * * when called, dev_id is a pointer at the soft state structure for this instance * * as of the 2.3.x kiobuf version, we use mutex and conditional variable for SMP * compatibility * * 2.6.x adds an interrupt claimed return value */ static irqreturn_t ihcp_intr(int irq, void *dev_id IHCP__PT_REGS) { struct ihcp_unit_t *unit_p; volatile u_long temp; int instance; int wake_up_needed; /* * get the unit struct pointer this way to avoid parameter type * complaints by the compiler, get instance for debugging, * clear wake up needed flag */ unit_p = (struct ihcp_unit_t *) dev_id; instance = unit_p->instance; wake_up_needed = 0; DPRINTI("entering ihcp_intr (may not be our interrupt)"); /* * grab mutex - in case strategy and int thread are running on different cpu's ??? */ ihcp_mutex_lock(&unit_p->mutex); /* * get plx interrupt control/status register and check for interrupt * check for master interrupt enable bit and either local int rq or dma int rq */ temp = PLX_GETL(PLX_INT_CSTAT); if ((temp & PCI_INTERRUPT_ENABLE) && ((temp & LOCAL_INTERRUPT) || (temp & DMA_0_INTERRUPT))) { DPRINTI("interrupt claimed, unit_p = 0x%p", unit_p); /* * force off dma enable, interrupt enables, and ihcp interrupt mask bits * which should release the interrupt request * * then (after masks are off) toggle reset int flag bit on & off to clear * interrupt flag (which is a latch) * and do the same to the dma done interrupt */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); PLX_PUTL(PLX_INT_CSTAT, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_CLEAR_INTERRUPT); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * do register reads to get the previous writes to flush to the pci bus * so the writes that clear the interrupt will be complete before we * exit - we will hope that the optimizer doesn't delete these! * * may not be necessary on PC */ read_dump_0 = PLX_GETL(PLX_EEPROM_USER); read_dump_1 = IHCP_GETL(IHCP_MODE); /* * clear sleeping flag so sleeping process will know we got a normal interrupt * set wake up needed flag */ unit_p->sleeping = 0; wake_up_needed = 1; } /* * release mutex */ ihcp_mutex_unlock(&unit_p->mutex); if (wake_up_needed) { /* * wake up sleeping process */ DPRINTI("claiming int and waking up sleeping process"); ihcp_cond_broadcast(&unit_p->cond); return IRQ_HANDLED; } else { DPRINTI("interrupt not claimed"); return IRQ_NONE; } } /* * strategy expects the virtual address of the user buffer (portion) to be output * at this point, it must be quad byte aligned and a byte count that must be 0 mod 4 * * if we are in user buffer DMA mode, strategy will map and pin the buffer, construct * an sg list of buffer pages, map the pages to the bus (using the dynamic mapping stuff), * build the dma scatter-gather list in iopb memory, synch the iopb memory, and do a * single dma block output * * if we are in copy user buffer mode ( kernel is configured for >4G of RAM) * strategy will copy the to kernel memory buffer, and do dma from that buffer * * if the count parameter is zero, strategy will wait for fifo less than 1/2 full, * but not do any output * * THERE ARE SOME ERROR AND OTHER RETURNS IN THIS CODE - IF WE EVER NEED A MUTEX, GATHER * THEM WITH GOTOs, SO WE WON'T ESCAPE OUT THE SIDE OF THE MUTEX -- BE CAREFUL TO UNMAP * THINGS ON ERROR (IF ANYTHING WAS MAPPED AT ERROR TIME) */ static int ihcp_strategy(struct ihcp_unit_t *unit_p, char *dma_buf_p, int dma_count) { int instance; unsigned int temp_mode; unsigned int temp_dev_ctrl; int wait_return; int error; int doing_dma; instance = unit_p->instance; DPRINTI("entering strategy, unit_p = 0x%p, buf_p = 0x%p, count = 0x%x", unit_p, dma_buf_p, dma_count); /* * make sure buffer is quad byte aligned and count is zero mod four * this is not strictly necessary, but makes for a more generic strategy * routine and check for max allowable size */ if ((u_long) dma_buf_p & 0x3) { EPRINTI("dma buffer not quad word aligned!"); return -EINVAL; } if (dma_count & 0x3) { EPRINTI("dma count not 0 mod 4!"); return -EINVAL; } if (dma_count > ihcp_max_buf_bytes) { EPRINTI("dma count too large!"); return -EINVAL; } /* * set flags to indicate dma wait - even if we don't wait, just so flags * will reflect that we were in strategy * error flags are not cleared here so they will survive * multiple calls to strategy by write */ unit_p->unit_flags |= IHCP_DMA_WAIT; /* * we should now have an aligned buffer, with 0 or more bytes in it * if total buffer size (remaining) is less than 4, skip dma stuff, * and wait for 3) { doing_dma = 1; /* * map and pin - or copy - user buffer */ if(ihcp_map_or_copy_buf(unit_p, instance, dma_buf_p, dma_count, WRITE)) { WPRINTI("ihcp_map_or_copy_failure!"); return -EIO; } if(ihcp_maplist_to_sg_list(unit_p, instance)) { WPRINTI("ihcp_maplist_to_sg_list failure!"); ihcp_unmap_on_error(unit_p, instance); return -EIO; } /* * map the sg list (which may combine contiguous entries * there is no error return, as I am told that the mapping resources are huge */ unit_p->nr_iopbs = pci_map_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, PCI_DMA_TODEVICE); DPRINTI("scatter list mapped, nr_iopbs = 0x%x", unit_p->nr_iopbs); /* * synch iopb memory for cpu before accessing it - this is commented out * since we don't care if it is up to date, since we will overwrite * it and sync for device - might need to use it later if kernel gods * assign some necessary side-effects pci_dma_sync_single_for_cpu(unit_p->pci_dev_p, unit_p->dma_handle, unit_p->nr_iopbs * IOPB_SIZE, PCI_DMA_TODEVICE); */ /* * convert sg list to iopb list for plx dma engine */ if(ihcp_sg_list_to_iopb_list(unit_p, instance)) { EPRINTI("ihcp_sg_list_to_iopb_list failure!"); pci_unmap_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, PCI_DMA_TODEVICE); ihcp_unmap_on_error(unit_p, instance); return -EIO; } /* * synch iopb memory for dma - NOP on a pc - necessary on other * architectures to ensure coherence betweene list and device * synch only as much iopb space as was touched this time */ pci_dma_sync_single_for_device(unit_p->pci_dev_p, unit_p->dma_handle, unit_p->nr_iopbs * IOPB_SIZE, PCI_DMA_TODEVICE); DPRINTI("iopb memory synched for DMA"); /* * program plx dma logic on board - in chaining mode * buffer is already aligned, and count masked * * make sure that any left over dma done interrupt * is cleared */ DPRINTI("programming PLX chip for DMA"); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_CLEAR_INTERRUPT); /* mode register */ PLX_PUTL(PLX_DMA_MODE_0, DMA_BUS_32_BIT | DMA_WAIT_1 | DMA_BURST_ENABLE | DMA_CHAIN_ENABLE | DMA_DONE_INTERRUPT_ENB | DMA_LOCAL_ADD_HOLD | DMA_DEMAND_MODE); /* output, list in pci mem, list pointer */ PLX_PUTL(PLX_DMA_DESC_PTR_0, DMA_CHAIN_IN_PCI_MEM | unit_p->dma_handle); /* start DMA in two steps - PLX bug */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_ENABLE); /* actually fire up xfer */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_ENABLE | DMA_START); /* * the 9060 drives the local interrupt out true when a dma * interrupt happens * * we have to loop that output back to the local interrupt input * external to the chip to get a dma interrupt to the pci bus */ /* * save processor flags, disable interrupts, and take spinlock w/mutex * before enabling board interrupts! * * THERE ARE TWO PLACES IN STRATEGY WHERE THIS MAY BE DONE, * BUT ONLY ONE PLACE WHERE FLAGS ARE RESTORED */ DPRINTI("disabling cpu ints and enabling board dma done int"); ihcp_mutex_lock(&unit_p->mutex); PLX_PUTL(PLX_INT_CSTAT, PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE | LOCAL_INT_OUT_ENABLE | LOCAL_DMA_0_INT_ENABLE); } else { doing_dma = 0; DPRINTI("no dma, checking for <1/2 full"); /* * we need to wait for fifo less than half full * it may already be less than half full - if so * return good status */ if (IHCP_GETL(IHCP_INTERFACE_STATUS) & FIFO_NOT_HALF_FULL) return 0; DPRINTI("not <1/2 full, setting up for interrupt"); /* * clear any left over interrupt flag, then * enable plx and ihcp interrupt for fifo less than half full * we don't or into ihcp or plx mask reg since we know exactly * which ints to enb */ IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * save processor flags, disable interrupts, and take spinlock w/mutex * before enabling board interrupts! * * THERE ARE TWO PLACES IN STRATEGY WHERE THIS MAY BE DONE, * BUT ONLY ONE PLACE WHERE FLAGS ARE RESTORED */ DPRINTI("disabling cpu ints, and enabling board fifo < 1/2 full int"); ihcp_mutex_lock(&unit_p->mutex); IHCP_PUTL(IHCP_INTERRUPT_MASK, FIFO_NOT_HALF_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, (PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE)); } /* * we arrive here after setting up for a dma wait, or a less than half full wait * * set sleeping flag - so we can detect abnormal sleep termination later * (int code will clear sleeping flag) * * board interrupts are already enabled, and processor interrupts disabled, * to avoid race conditions - sleep will re-enable * * snooze until done or sleeping = 1; DPRINTI("calling cond_timed_wait_rel, jiffies = %ld, unit_p->dma_time = %d", jiffies, unit_p->dma_time); wait_return = ihcp_cond_timed_wait_rel(&unit_p->cond, &unit_p->mutex, unit_p->dma_time); /* * make sure interrupt enables are off and pause dma (which will normally * already be done) */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); IHCP_PUTL(IHCP_INTERRUPT_MASK, 0); PLX_PUTL(PLX_INT_CSTAT, 0); ihcp_mutex_unlock(&unit_p->mutex); /* * if we were doing dma, and * if dma is not done (after sig or timeout), force an abort * and issue a master clear to flush ihcp buffers * * abort may take some time to complete, so use delay and * register read to give it some time * * we have to be sure we were actually doing dma - since we need * to unmap the sg list and user buf (if used) */ if (doing_dma && !(PLX_GETL(PLX_DMA_CMD_STAT_BOTH) & DMA_DONE)) { DPRINTI("dma not done after ihcp_cond_timed_wait_rel() return"); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_ABORT); /* * get mode and dev control regs so we can restore them after a mclr * also restore the byte ordering * * mclr clears the fifos, and also makes them assert dma rq * but...mclr holds off dma rq while mclr is asserted, and since the 9060 * likes a synchronous dma rq, we will wait a while before hitting mclr * * 10us of delay seems like a lot, but we will only be in this code * following an error * * it is not clear whether the 9060 needs dma rq in order to * finish an abort * * if it does, it may transfer a word or two after the mclr, * so we will repeat the mclr later */ udelay(5); temp_dev_ctrl = IHCP_GETL(IHCP_DEVICE_CONTROL); temp_mode = IHCP_GETL(IHCP_MODE); IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * force write cache to flush - so delay appears on the bus */ read_dump_0 = IHCP_GETL(IHCP_INTERRUPT_MASK); udelay(5); IHCP_PUTL(IHCP_INTERFACE_CONTROL, MASTER_CLEAR); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); IHCP_PUTL(IHCP_DEVICE_CONTROL, temp_dev_ctrl); IHCP_PUTL(IHCP_MODE, temp_mode); ihcp_set_byte_order(unit_p); /* * make sure dma actually says done */ if (!(PLX_GETL(PLX_DMA_CMD_STAT_BOTH) & DMA_DONE)) { EPRINTI("dma not done after abort!"); pci_unmap_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, PCI_DMA_TODEVICE); ihcp_unmap_on_error(unit_p, instance); return -EIO; } } /* * now we can safely make sure that the done interrupt is clear, * and reset the ihcp interrupt flag * * (the above is probably not necessary after normal dma done) */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_CLEAR_INTERRUPT); IHCP_PUTL(IHCP_INTERFACE_CONTROL, CLEAR_INTERRUPT_FLAG); IHCP_PUTL(IHCP_INTERFACE_CONTROL, 0); /* * if dma was performed, unmap the sg list (NOP on pc, release iommu mappings * on other machines) unmap sg wants the original sg list length * * unmap user buffer (if used) */ if (doing_dma) { DPRINTI("unmapping sg list and user buffer (if mapped) after dma"); pci_unmap_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, PCI_DMA_TODEVICE); if(ihcp_unmap_or_copy_buf(unit_p, instance)) { EPRINTI("ihcp_unmap_or_copy_buf failure!"); return -EIO; } } /* * find out why we woke up */ if (unit_p->sleeping) { /* * flag some kind of error - in case both the following tests fail */ unit_p->sleeping = 0; error = -EIO; DPRINTI("abnormal return from ihcp_cond_timed_wait_rel()!"); /* * check for timeout */ if (wait_return == IHCP_COND_WAIT_TIMEOUT) { /* * timeout is always an error so print message and return error code to write * and flag timeout in unit flags */ EPRINTI("timeout while waiting for interrupt!"); unit_p->unit_flags |= IHCP_DMA_TIMEOUT; error = -EIO; } /* * check for signal if not masked (blocked is a signal bitmask) */ if (wait_return == IHCP_COND_WAIT_SIGNAL) { /* * flag signal received and print error message */ WPRINTI("signal received while waiting for interrupt"); unit_p->unit_flags |= IHCP_SIG_RECEIVED; error = -EINTR; } /* * save latched registers and do a soft reset * restore saved registers and byte ordering * * this is added for 2.6.x */ temp_dev_ctrl = IHCP_GETL(IHCP_DEVICE_CONTROL); temp_mode = IHCP_GETL(IHCP_MODE); plx_soft_reset(unit_p); IHCP_PUTL(IHCP_DEVICE_CONTROL, temp_dev_ctrl); IHCP_PUTL(IHCP_MODE, temp_mode); ihcp_set_byte_order(unit_p); return error; } /* end of if sleeping * * if we get here - just return good status * there is no way to figure a residual count with the hardware at hand * so we don't even try */ return 0; } /* * mutex and conditional variable routines - were in ikon_mutex.c * Kaz Kylheku & wdw */ void ihcp_mutex_init(ihcp_mutex_t * mx) { spin_lock_init(&mx->spin); } void ihcp_mutex_lock(ihcp_mutex_t * mx) { unsigned long flags; spin_lock_irqsave(&mx->spin, flags); mx->flags = flags; } void ihcp_mutex_unlock(ihcp_mutex_t * mx) { unsigned long flags = mx->flags; spin_unlock_irqrestore(&mx->spin, flags); } void ihcp_cond_init(ihcp_cond_t * cv) { init_waitqueue_head(&cv->queue); } int ihcp_cond_timed_wait_rel(ihcp_cond_t * cv, ihcp_mutex_t * mx, long jiff_delta) { wait_queue_t wait; int ret; long remaining; init_waitqueue_entry(&wait, current); add_wait_queue(&cv->queue, &wait); current->state = TASK_INTERRUPTIBLE; ihcp_mutex_unlock(mx); remaining = schedule_timeout(jiff_delta); ret = remaining ? signal_pending(current) ? IHCP_COND_WAIT_SIGNAL : IHCP_COND_WAIT_SUCCESS : IHCP_COND_WAIT_TIMEOUT; current->state = TASK_RUNNING; remove_wait_queue(&cv->queue, &wait); ihcp_mutex_lock(mx); return ret; } void ihcp_cond_broadcast(ihcp_cond_t * cv) { wake_up_interruptible(&cv->queue); } /* * do a soft reset of the plx chip - also clears the ihcp logic * * the pci_dev handle, and int_level elements of the unit structure * MUST be set before calling this routine! * * ALSO - THIS ROUTINE MUST NOT BE CALLED FROM THE INTERRUPT SIDE OF THINGS!! * (it calls schedule to do a delay) */ static void plx_soft_reset(struct ihcp_unit_t *unit_p) { u_long wait_j; /* wait jiffies */ /* * soft reset clears the local config registers, which turns off add space 0 accesses * * so we will force an eeprom reload, and wait a while (0.2s) to avoid too many retry * cycles while the eeprom is reloading - we will use delay, not drv_usec_wait, so we * don't tie up the cpu * * do a read dump before the reload to force any write buffer to flush, so our wait * timing is reflected on the bus (hopefully). if we try to access the plx chip * during reload, retry cycles happen, and a short bus timer could cause a panic * * an earlier rev cleared the int line byte in the configuration registers during * soft reset, but that is not supposed to be true anymore * * IT SEEMS THAT IT IS STILL TRUE - SO RESTORE THE INT LINE AFTER SOFT_RESET * * soft reset also clears the endian setting and other latches so the calling * could should set/reset as appropriate * * we probably don't have to clear the config reload bit after writing it, * but we will to be sure */ /* * there is really no good way to return an error from here - so just put it * on the console and return without touching anything */ PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) | PLX_SOFT_RESET)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) & ~PLX_SOFT_RESET)); read_dump_0 = PLX_GETL(PLX_INT_CSTAT); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) | CONFIGURATION_RELOAD)); /* * wait ... */ wait_j = jiffies + HZ / 5; while (time_before(jiffies, wait_j)) schedule(); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) & ~CONFIGURATION_RELOAD)); pci_write_config_byte(unit_p->pci_dev_p, PCI_INTERRUPT_LINE, unit_p->int_level); DPRINT("instance %d: soft reset and configuration reload complete", unit_p->instance); /* * soft reset leaves pci master int enable on, so turn it off for safety * don't bother with oring bits - just hammer them all off! */ PLX_PUTL(PLX_INT_CSTAT, 0); /* * restore endian mode default * the reset sets us back to little-endian * nothing to do if still little-endian */ ihcp_set_byte_order(unit_p); /* * disable address space 0 pre-fetch */ PLX_PUTL(PLX_ADD_0_ROM_DESC, PLX_GETL(PLX_ADD_0_ROM_DESC) | ADD_0_PREFETCH_DISABLE); } /* * establish driver and board defaults - some of these will be used to establish * initial configuration on a per-board basis * * there are a lot of these things - to try to stay consistent with solaris driver * and conf file. we could probably eliminate one level of defaults (in unit structure) * but this way will avoid some code modification, and let the insmod parameters match * the solaris .conf parameters * * the config variables that end in _def may be later modified by ioctl calls on a per * board basis * * examine the variables that may be set during insmod - if not -1, and within * permitted range, use the insmod supplied value - if -1, use the default * * some of the range checks look redundant ( .. >= 0 ..), but I prefer to keep the * insmod set detection separate from the range check, and not assume anything */ static void ihcp_set_global_defaults(void) { DPRINT("entering ihcp_set_global_defaults"); ihcp_byte_order_def = BYTE_ORDER_DEF; if (byte_order_def >= 0) { if ((byte_order_def == LOW_BYTE_FIRST) || (byte_order_def == HIGH_BYTE_FIRST)) ihcp_byte_order_def = byte_order_def; else WPRINT("byte_order_def out of range! using default"); } DPRINT("ihcp_byte_order_def = 0x%x", ihcp_byte_order_def); /* * select one of the four versatec speeds as a default */ ihcp_vers_speed_def = VERS_SPEED_DEF; if (vers_speed_def >= 0) { if ((vers_speed_def >= 0) && (vers_speed_def <= 3)) ihcp_vers_speed_def = vers_speed_def; else WPRINT("vers_speed_def out of range! using default"); } DPRINT("ihcp_vers_speed_def = 0x%x", ihcp_vers_speed_def); /* * select one of the four centronics speeds as the default */ ihcp_cent_speed_def = CENT_SPEED_DEF; if (cent_speed_def >= 0) { if ((cent_speed_def >= 0) && (cent_speed_def <= 3)) ihcp_cent_speed_def = cent_speed_def; else WPRINT("cent_speed_def out of range! using default"); } DPRINT("ihcp_cent_speed_def = 0x%x", ihcp_cent_speed_def); /* * select power-up print/plot mode for versatec boards * 0 = print, 1 = plot (probably should use #macros, not numeric values, but ... */ ihcp_mode_def = MODE_DEF; if (mode_def >= 0) { if ((mode_def == 0) || (mode_def == 1)) ihcp_mode_def = mode_def; else WPRINT("mode_def out of range! using default"); } DPRINT("ihcp_mode_def = 0x%x", ihcp_mode_def); /* * set default time to wait for dma to complete */ ihcp_dma_time_def = DMA_TIME_DEF; if (dma_time_def >= 0) { if ((dma_time_def >= DMA_TIME_MIN) && (dma_time_def <= DMA_TIME_MAX)) ihcp_dma_time_def = dma_time_def; else WPRINT("dma_time_def out of range! using default"); } DPRINT("ihcp_dma_time_def = 0x%x", ihcp_dma_time_def); /* * set default time to wait for fifo empty and board and device ready */ ihcp_fifo_time_def = FIFO_TIME_DEF; if (fifo_time_def >= 0) { if ((fifo_time_def >= FIFO_TIME_MIN) && (fifo_time_def <= FIFO_TIME_MAX)) ihcp_fifo_time_def = fifo_time_def; else WPRINT("fifo_time_def out of range! using default"); } DPRINT("ihcp_fifo_time_def = 0x%x", ihcp_fifo_time_def); /* * set max board number - this will determine how much we kmalloc for the * unit structures. we will do a sanity check, and require this number to * be a single digit */ ihcp_max_boards = MAX_BOARDS_DEF; if (max_boards >= 0) { if ((max_boards >= 0) && (max_boards <= 9)) ihcp_max_boards = max_boards; else WPRINT("max_boards out of range! using default"); } DPRINT("ihcp_max_boards = %d", ihcp_max_boards); /* * set max_phys_order and several values * that derive from it * * max buffer pages = 2**max phys order * max bytes per dma xfer = max buffer pages * PAGE_SIZE * max size of scatter/gather list = max buffer pages + 1 * * these calculations must track w/copy buf and user buf map size as well * * size of iopb memory allocated = max sg size * IOPB_SIZE * * we will check for a request less than zero, but not limit the * upper end - USER BEWARE! */ ihcp_max_phys_order = MAX_PHYS_ORDER_DEF; if (max_phys_order >= 0) ihcp_max_phys_order = max_phys_order; ihcp_max_buf_pages = (1 << ihcp_max_phys_order); ihcp_max_buf_bytes = ihcp_max_buf_pages * PAGE_SIZE; ihcp_max_sg_length = ihcp_max_buf_pages + 1; ihcp_iopb_mem_size = ihcp_max_sg_length * IOPB_SIZE; DPRINT("max_phys_order = 0x%x, max_bytes = 0x%x,\n" " max_sg_length = 0x%x, iopb_mem_size = 0x%x", ihcp_max_phys_order, ihcp_max_buf_bytes, ihcp_max_sg_length, ihcp_iopb_mem_size); } /* * do the per-board initialization * return 0 if successful, error if not */ static int ihcp_init_one_board(struct ihcp_unit_t *unit_p, int instance) { u32 temp; u_char c_temp; /* * init flags, mutex, and conditional variable */ ihcp_mutex_init(&unit_p->mutex); ihcp_cond_init(&unit_p->cond); unit_p->sleeping = 0; unit_p->unit_attached = 0; unit_p->unit_open = 0; spin_lock_init(&unit_p->open_lock); unit_p->unit_flags = 0; /* * allocate dma resources - iopb list memory, sg list memory, user buffer map list, * and copy buffer if direct user dma not to be used - and set dma mask */ if(ihcp_dma_alloc(unit_p, instance)) { EPRINT("ihcp_dma_alloc error! board not attached!"); return -EIO; } /* * grind through the config registers to get register base addresses and other * values * * use the new pci interface, but keep the rest of the old code (lazy) * * the base address registers contain the bus addresses of our two register sets - * plx and ihcp * unfortunately, the plx register set is smaller than a page, so bios may align it * on an address that isn't a page boundary * * ioremap doesn't tolerate that, so we have to figure out the appropriate (maybe * lower) page boundary and the appropriate size to ask for * we will save the mapped base and the actual virtual pointer to the register set * * I hope linux allows multiple processes to map the same bus page, or we may * clobber someone else */ /* * make sure io and memory access is enabled * make sure that the pci master enable bit is set * * pci_enable_device does some things on non-x86 arch's * that must be done before accessing device resources registers */ if(pci_enable_device(unit_p->pci_dev_p)) { EPRINTI("pci_enable_device error! board not attached"); ihcp_dma_free(unit_p, instance); return -EIO; } pci_set_master(unit_p->pci_dev_p); DPRINTI("master enable bits set"); /* * map the board's registers for slave access */ if(ihcp_map_regs(unit_p, instance)) { EPRINTI("ihcp_map_regs error! board not attached"); ihcp_dma_free(unit_p, instance); return -EIO; } /* * get (possibly) mapped int level from pci_dev for use in kernel calls, and * the hardware level from the board for use when restoring the level after * an eeprom reload */ unit_p->pci_dev_int_level = unit_p->pci_dev_p->irq; if (pci_read_config_byte (unit_p->pci_dev_p, PCI_INTERRUPT_LINE, (u_char *) & c_temp)) { EPRINTI("error reading pci interrupt line!"); ihcp_unmap_regs(unit_p, instance); ihcp_dma_free(unit_p, instance); return -EIO; } unit_p->int_level = c_temp; DPRINTI("board int line = 0x%x, pci_dev int line = 0x%x", unit_p->int_level, unit_p->pci_dev_int_level); /* * save device and vendor ids and rev level */ if (pci_read_config_dword (unit_p->pci_dev_p, PCI_VENDOR_ID, (int *) &temp)) { EPRINTI("error reading pci vendor id! board not attached!"); ihcp_unmap_regs(unit_p, instance); ihcp_dma_free(unit_p, instance); return -EIO; } unit_p->dev_and_vendor_id = temp; DPRINTI("device and vendor id = 0x%x", temp); if (pci_read_config_byte (unit_p->pci_dev_p, PCI_REVISION_ID, (char *) &temp)) { EPRINTI("error reading revision id! board not attached!"); ihcp_unmap_regs(unit_p, instance); ihcp_dma_free(unit_p, instance); return -EIO; } unit_p->revision_id = temp & 0xff; DPRINTI("revision id = 0x%x", temp & 0xff); /* * do a soft reset of the plx chip - which also clears the ihcp logic * this will also cause an init to be sent to the device * (soft reset function also clears big-endian if set, but the * soft reset code will restore it - as long as it is correctly * set in the unit struct prior to calling soft_reset) */ plx_soft_reset(unit_p); /* * set defaults on a per-board basis - MUST BE DONE AFTER SOFT RESET NOT BEFORE! * soft reset clears latches and BYTE ORDERING bits! */ ihcp_set_board_defaults(unit_p, instance); /* * get a plx register and dprint it for a sanity check - make sure we can get to plx regs */ DPRINTI("sanity check: PLX EEPROM reg = 0x%x (s.b. xxxx767e)", PLX_GETL(PLX_EEPROM_USER)); /* * get the board strapping and save it * zeros rest of flag register at same time */ unit_p->unit_flags = INTERFACE_STRAP_MASK & IHCP_GETL(IHCP_DEVICE_STATUS); DPRINTI("sanity check: interface strapping = 0x%x", IHCP_BOARD_MASK & unit_p->unit_flags); /* * register the interrupt line for this board & link to our interrupt handler * pass our unit pointer to the interrupt code * * we will use our node name as a label for the interrupt - that way * /proc/interrupts will have a unique name per interrupt (per board) rather * than all of them being named ihcp (we could use this when mknod is invoked) * * we have to keep the name in the unit structure, since request_irq just * saves the pointer, and doesn't copy the string - so by the time * /proc/interrupts gets read, the stack has changed, and the name would be * garbage if not kept in "permanent" memory * * (thank you Mr. Rubini!) */ (void) sprintf((char *) unit_p->minor_node_name, "ihcp%d", instance); if (request_irq (unit_p->pci_dev_int_level, ihcp_intr, IRQF_SHARED, (const char *) unit_p->minor_node_name, (void *) unit_p)) { EPRINTI("error requesting interrupt! board not attached!"); ihcp_unmap_regs(unit_p, instance); ihcp_dma_free(unit_p, instance); return -EIO; } if ((unit_p->unit_flags & IHCP_BOARD_MASK) == IHCPIO_CENT) IPRINTI("board attached, irq = 0x%x, strapped for CENTRONICS", unit_p->pci_dev_int_level); else IPRINTI("board attached, irq = 0x%x, strapped for VERSATEC", unit_p->pci_dev_int_level); return 0; } /* * sort out and save the defaults on a per-board basis * some of this is probably redundant, but we are trying * to stay with the solaris code as much as possible * * the ranges of any insmod-supplied parameters have already been checked */ static void ihcp_set_board_defaults(struct ihcp_unit_t *unit_p, int instance) { unsigned int temp; DPRINTI("entering routine"); switch (ihcp_vers_speed_def) { case 0: unit_p->vers_speed_def = SPEED_0; break; case 1: unit_p->vers_speed_def = SPEED_1; break; case 2: unit_p->vers_speed_def = SPEED_2; break; case 3: unit_p->vers_speed_def = SPEED_3; break; } DPRINTI("versatec speed default set to 0x%x", unit_p->vers_speed_def); switch (ihcp_cent_speed_def) { case 0: unit_p->cent_speed_def = SPEED_0; break; case 1: unit_p->cent_speed_def = SPEED_1; break; case 2: unit_p->cent_speed_def = SPEED_2; break; case 3: unit_p->cent_speed_def = SPEED_3; break; } DPRINTI("centronics speed default set to 0x%x", unit_p->cent_speed_def); if (ihcp_mode_def == 1) unit_p->mode_def = PLOT_MODE; else unit_p->mode_def = NORM_PRINT_MODE; DPRINTI("mode default set to 0x%x", unit_p->mode_def); unit_p->dma_time_def = ihcp_dma_time_def; DPRINTI("dma time default set to 0x%x", unit_p->dma_time_def); unit_p->fifo_time_def = ihcp_fifo_time_def; DPRINTI("fifo time default set to 0x%x", unit_p->fifo_time_def); /* * set the data byte ordering default mode * the native ordering for the board is low byte first * * if high byte first mode is selected, the single byte data out and command out * bytes must be sent to the fifos in the HIGH byte of the longword! * * ordering is set here, and stored in the unit structure, since it will affect * all later accesses to the 8 bit data and command registes * * we only take action if high_byte_first is selected, since the preceeding reset * should have set us to low_byte_first mode * MUST CALL SOFT RESET BEFORE THIS INIT ROUTINE!!!! * * setting high byte first mode requires turning on the byte_swizzle bit in the * ihcp logic, and TURNING OFF the user output bit in the plx logic */ if (ihcp_byte_order_def == HIGH_BYTE_FIRST) DPRINTI("setting ordering mode to high byte first"); else DPRINTI("ordering mode left at low byte first"); unit_p->byte_order = ihcp_byte_order_def; ihcp_set_byte_order(unit_p); /* * write the default handshake speeds for versatec and centronics boards (as * determined by strapping), * respectively, and the default print/plot mode for versatec boards * the print/plot mode is also saved in the unit structure, since it may be * referred to later - and isn't visible in the board's registers * * NOTE THAT THE SAME DEFAULTS ARE USED FOR ALL BOARDS OF THE SAME TYPE!!!!!! */ if ((unit_p->unit_flags & IHCP_BOARD_MASK) != IHCPIO_CENT) { /* * versatec - set speed and print/plot mode */ DPRINTI("setting default versatec speed (on board) to 0x%x", unit_p->vers_speed_def); DPRINTI("setting default versatec print/plot mode (on board) to 0x%x", unit_p->mode_def); IHCP_PUTL(IHCP_MODE, ((IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->vers_speed_def)); temp = SET_VERSATEC_MODE | unit_p->mode_def; if (unit_p->byte_order == HIGH_BYTE_FIRST) temp = temp << 24; IHCP_PUTL(IHCP_COMMAND_OUT, temp); unit_p->mode = unit_p->mode_def; } else { /* * centronics - set speed only */ DPRINTI("setting default centronics speed (on board) to 0x%x", unit_p->cent_speed_def); IHCP_PUTL(IHCP_MODE, ((IHCP_GETL(IHCP_MODE) & ~SPEED_MASK) | unit_p->cent_speed_def)); } /* * set saved data streaming mode - for old vme getregs call compatibility, since * we can't read data streaming status from the logic on the other side of the * fifo in the pci card */ unit_p->data_stream_mode = DATA_STREAMING_OFF; } /* * allocate dma resources - iopb list memory, sg list memory, user buffer map list, * and copy buffer if direct user dma not to be used - and set dma mask */ static int ihcp_dma_alloc(struct ihcp_unit_t *unit_p, int instance) { int i; DPRINTI("entering routine"); /* * get contiguous pages of kernel memory to contain to iopbs (dma chaining list) * GFP_KERNEL should get us contiguous memory, don't set dma flag so it won't be * forced into low memory */ unit_p->iopb_base_p = kmalloc(ihcp_iopb_mem_size, GFP_KERNEL); if (!unit_p->iopb_base_p) { EPRINTI("kmalloc failure (iopb memory)!"); return -1; } DPRINTI("iopb memory allocated, virtual address = 0x%p", unit_p->iopb_base_p); /* * check for alignment - we could have asked for more and forced it, * but I don't think the mem caches have any * chunks w/smaller alignments than we need */ if ((unsigned long) (unit_p->iopb_base_p) & (IOPB_SIZE - 1)) { EPRINTI("iopb memory not IOPB_SIZE aligned!"); kfree(unit_p->iopb_base_p); return -1; } /* * inform the kernel what our dma engine can address * (32 bits at this time/with this hardware) */ if (pci_set_dma_mask(unit_p->pci_dev_p, IHCP_DMA_MASK)) { EPRINTI("DMA addressing not compatible!"); kfree(unit_p->iopb_base_p); return -1; } /* * get the bus address of iopb memory using the dynamic DMA mapping stuff, * (doesn't amount to much on a PC, but will help w/compatibility on SPARC, * and other arch's w/iommu hardware * * make sure it isn't larger than our dma addressing capability * if this fails, the kernel pci routines are borked, or we didn't * specify our DMA mask correctly */ unit_p->dma_handle = pci_map_single(unit_p->pci_dev_p, unit_p->iopb_base_p, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); if(unit_p->dma_handle > IHCP_DMA_MASK) { EPRINTI("dma_handle out of range!"); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } DPRINTI("iopb memory mapped, dma_handle = 0x%lx", (unsigned long)unit_p->dma_handle); /* * get space for the scatterlist ... this probably isn't necessary on a pc, since * we should be able to convert the user buff page mappings direcly into bus adds, * and build the iopb list, but to try for compatibility with other architectures, * we will go through a scatterlist on the way to building an iopb list * * the newer architectures may take advantage of IOMMU mapping, others may * implement bounce buffers via the scatterlist * * as of 2.6.24, it is possible to chain scatterlist pages - we will just * ask for more than a page if that's what the user really wants and not * use the chaining feature. if the user's unreasonable demand can't be * met, just let kmalloc barf * * we now have to zero the allocated memory, since the kernel gods have added new * .page and .offset to the scatterlist struct, and if .page AND .address are either * both zero or both non-zero, they BUG()! (maybe not in 2.6...) */ unit_p->sg_list_p = kmalloc(ihcp_max_sg_length * sizeof(struct scatterlist), GFP_KERNEL); if (unit_p->sg_list_p == NULL) { EPRINTI("can't allocate scatter list (kmalloc error)!"); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } memset(unit_p->sg_list_p, 0, ihcp_max_sg_length * sizeof(struct scatterlist)); DPRINTI("scatter list allocated, sg_list_p = 0x%p", unit_p->sg_list_p); /* * allocate an array of page pointers to hold ptrs to mapped * user pages * same size as max sg list since user bfr alignment may require * an extra page */ if((unit_p->maplist_p = kmalloc(ihcp_max_sg_length * sizeof(*unit_p->maplist_p), GFP_KERNEL)) == NULL) { EPRINTI("can't allocate maplist!"); kfree(unit_p->sg_list_p); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } DPRINTI("maplist allocated, address = 0x%p", unit_p->maplist_p); /* * if a copy buffer is required, allocate it here and stuff the maplist w/its * pages * * we only allocate just enough pages to satisfy the max buffer size, since we will always * copy to the buffer starting at offset = 0, so we don't need an extra page to handle * alignment (like the maplist and sg list do) */ if (IHCP_COPY_BUF) { DPRINTI("allocating copy buffer"); unit_p->dma_copy_buf_p = (char *)__get_free_pages(GFP_KERNEL, ihcp_max_phys_order); if(unit_p->dma_copy_buf_p == NULL) { EPRINTI("can't allocate copy buffer!"); kfree(unit_p->maplist_p); kfree(unit_p->sg_list_p); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } DPRINTI("copy buffer allocated, address = 0x%p", unit_p->dma_copy_buf_p); /* * fill maplist array with pointers to buffer page structures * since we are in copy mode, these pointers will never change */ for (i=0; imaplist_p[i] = virt_to_page(unit_p->dma_copy_buf_p + ( i * PAGE_SIZE)); DPRINTI("maplist loaded w/copy buff pages"); } else { DPRINTI("user DMA mode, no copy buffer allocated"); unit_p->dma_copy_buf_p = NULL; } return 0; } /* * free the above */ static void ihcp_dma_free(struct ihcp_unit_t *unit_p, int instance) { DPRINTI("entering routine"); if(unit_p->dma_copy_buf_p) free_pages((unsigned long)unit_p->dma_copy_buf_p, ihcp_max_phys_order); kfree(unit_p->maplist_p); kfree(unit_p->sg_list_p); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, ihcp_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); } /* * map the board registers for slave access */ static int ihcp_map_regs(struct ihcp_unit_t *unit_p, int instance) { u_long bus_address; /* temp base address */ u_long bus_base; /* temp address base */ u_long bus_offset; /* temp address offset */ DPRINTI("entering routine"); bus_address = unit_p->pci_dev_p->resource[0].start; DPRINTI("pci_base_address_0 (plx regs) = 0x%lx", bus_address); bus_address &= PCI_BASE_ADDRESS_MEM_MASK; bus_base = bus_address & ~(PAGE_SIZE - 1); bus_offset = bus_address & (PAGE_SIZE - 1); DPRINTI("plx bus add = 0x%lx, base = 0x%lx, offset = 0x%lx", bus_address, bus_base, bus_offset); unit_p->plx_page_base_p = ioremap(bus_base, PLX_REG_SIZE + bus_offset); unit_p->plx_base_p = unit_p->plx_page_base_p + bus_offset; if (unit_p->plx_page_base_p == NULL) { EPRINTI("can't ioremap plx registers!"); return -1; } DPRINTI("plx mapped base = 0x%p, virt addr = 0x%p", unit_p->plx_page_base_p, unit_p->plx_base_p); bus_address = unit_p->pci_dev_p->resource[2].start; DPRINTI("pci_base_address_2 (ihcp regs) = 0x%lx", bus_address); bus_address &= PCI_BASE_ADDRESS_MEM_MASK; bus_base = bus_address & ~(PAGE_SIZE - 1); bus_offset = bus_address & (PAGE_SIZE - 1); DPRINTI("ihcp bus add = 0x%lx, base = 0x%lx, offset = 0x%lx", bus_address, bus_base, bus_offset); unit_p->ihcp_page_base_p = ioremap(bus_base, IHCP_REG_SIZE + bus_offset); unit_p->ihcp_base_p = unit_p->ihcp_page_base_p + bus_offset; if (unit_p->ihcp_page_base_p == NULL) { EPRINTI("can't ioremap ihcp registers!"); iounmap(unit_p->plx_page_base_p); return -1; } DPRINTI("ihcp mapped base = 0x%p, virt addr = 0x%p", unit_p->ihcp_page_base_p, unit_p->ihcp_base_p); return 0; } /* * unmap the board's registers */ static void ihcp_unmap_regs(struct ihcp_unit_t *unit_p, int instance) { DPRINTI("entering routine"); iounmap(unit_p->ihcp_page_base_p); iounmap(unit_p->plx_page_base_p); } /* * check maplist values for max/min permissible */ static int ihcp_maplist_check(struct ihcp_unit_t *unit_p, int instance) { DPRINTI("entering routine"); if((unit_p->maplist_user_buf_p + unit_p->maplist_length) < unit_p->maplist_user_buf_p) { EPRINTI("buffer add + length overflow!"); return -EINVAL; } if(unit_p->maplist_nr_pages == 0) { EPRINTI("maplist_nr_pages = 0!"); return -EINVAL; } if(unit_p->maplist_nr_pages > ihcp_max_sg_length) { EPRINTI("maplist_nr_pages too large!"); return -EINVAL; } if(unit_p->maplist_length == 0) { EPRINTI("maplist_length = 0!"); return -EINVAL; } if(unit_p->maplist_length > ihcp_max_buf_bytes) { EPRINTI("maplist_length too large!"); return -EINVAL; } return 0; } /* * map and pin user buffer or copy user buffer to kernel buffer * maplist_rw_mode must be set before calling this * (might be cleaner to make that a parameter...) * * set the ->maplist_xx items to appropriate values for * either case * * CHECK FOR SANE VALUES in either case */ static int ihcp_map_or_copy_buf(struct ihcp_unit_t *unit_p, int instance, char *user_buf_p, int buf_size, int rw_mode) { DPRINTI("user_buf_p = 0x%p, size = 0x%x, rw_mode = 0x%x", user_buf_p, buf_size, rw_mode); unit_p->maplist_rw_mode = rw_mode; unit_p->maplist_user_buf_p = user_buf_p; /* not used in copy mode? */ if(unit_p->dma_copy_buf_p) { /* * we are in copy mode - if OUTPUT copy user data to buffer * set up maplist - we don't need to map since buffer already in kernel * offset always zero when using copy buffer */ unit_p->maplist_offset = 0; unit_p->maplist_length = buf_size; unit_p->maplist_nr_pages = (buf_size + PAGE_SIZE -1)/PAGE_SIZE; if(rw_mode != READ) { DPRINTI("copying user buffer to dma buffer"); if(ihcp_maplist_check(unit_p, instance)) { EPRINTI("maplist value(s) out of range!"); return -EINVAL; } if(copy_from_user(unit_p->dma_copy_buf_p, user_buf_p, buf_size)) { EPRINTI("copy_from_user error!"); return -EIO; } } } else { /* * we will be mapping and pinning the user buffer for direct DMA */ DPRINTI("mapping and pinning user buffer"); unit_p->maplist_offset = (unsigned long)user_buf_p & ~PAGE_MASK; unit_p->maplist_length = buf_size; unit_p->maplist_nr_pages = (unit_p->maplist_offset + buf_size -1 + ~PAGE_MASK) >> PAGE_SHIFT; if(ihcp_maplist_check(unit_p, instance)) { EPRINTI("maplist value(s) out of range!"); return -EINVAL; } if(ihcp_map_user_buf(unit_p, instance)) { EPRINTI("ihcp_map_user_buf failure!"); return -EIO; } } DPRINTI("buf_p = 0x%p, nr_pages = 0x%x, offset = 0x%x, length = 0x%x", unit_p->maplist_user_buf_p, unit_p->maplist_nr_pages, unit_p->maplist_offset, unit_p->maplist_length); return 0; } /* * unmap user buffer or copy kernel buffer to user buffer */ static int ihcp_unmap_or_copy_buf(struct ihcp_unit_t *unit_p, int instance) { DPRINTI("entering routine"); if(unit_p->dma_copy_buf_p) { /* * copy buffer mode, copy if input operation */ if(unit_p->maplist_rw_mode == READ) { DPRINTI("copying dma data to user buffer"); if(ihcp_maplist_check(unit_p, instance)) { EPRINTI("maplist value(s) out of range!"); return -EINVAL; } if(copy_to_user(unit_p->maplist_user_buf_p, unit_p->dma_copy_buf_p, unit_p->maplist_length)) { EPRINTI("copy_to_user error!"); return -EIO; } } } else { /* * direct user buffer dma mode, unmap user buf * maplist values already checked */ DPRINTI("unmapping user buffer"); ihcp_unmap_user_buf(unit_p, instance); } return 0; } /* * unmap if error requires undoing mapping - if copy mode, do nothing */ static void ihcp_unmap_on_error(struct ihcp_unit_t *unit_p, int instance) { DPRINTI("entering routine"); if(!unit_p->dma_copy_buf_p) { DPRINTI("calling ihcp_unmap_user_buf"); ihcp_unmap_user_buf(unit_p, instance); } else DPRINTI("copy buf mode, doing nothing"); } /* * map and pin user buffer for direct DMA * * code adapted from st.c */ static int ihcp_map_user_buf(struct ihcp_unit_t *unit_p, int instance) { int nr_pages_mapped; int i; DPRINT("entering routine"); /* * Try to fault in all of the necessary pages * * rw==READ means read from drive, write into memory area * 0 arg = don't force */ down_read(¤t->mm->mmap_sem); nr_pages_mapped = get_user_pages(current, current->mm, (unsigned long)unit_p->maplist_user_buf_p, unit_p->maplist_nr_pages, unit_p->maplist_rw_mode == READ, 0, unit_p->maplist_p, NULL); up_read(¤t->mm->mmap_sem); /* * result == nr pages requested if all pages mapped * or - if error */ if(nr_pages_mapped < unit_p->maplist_nr_pages) { EPRINTI("couldn't map all (any?) pages!"); if(nr_pages_mapped > 0) { for(i=0; i < nr_pages_mapped; i++) put_page(unit_p->maplist_p[i]); } return -EIO; } /* * st.c includes this call here, but I believe * it is included in get_user_pages w/2.6 * * for (i=0; i < nr_pages; i++) * flush_dcache_page(unit_p->maplist_p[i]); */ return 0; } /* * unmap user buffer * * (comment from st.c) * FIXME: cache flush missing for rw==READ * FIXME: call the correct reference counting function */ static void ihcp_unmap_user_buf(struct ihcp_unit_t *unit_p, int instance) { int i; DPRINT("entering routine"); for (i=0; i < unit_p->maplist_nr_pages; i++) { if (unit_p->maplist_rw_mode == READ) SetPageDirty(unit_p->maplist_p[i]); put_page(unit_p->maplist_p[i]); } } /* * convert maplist int sg list */ static int ihcp_maplist_to_sg_list(struct ihcp_unit_t *unit_p, int instance) { struct page **maplist; int map_nr_pages; int map_length; int map_offset; int temp_length; int i; DPRINT("entering routine"); /* * do a sanity check on the number of pages - it better not exceed the sg list max size */ if (unit_p->maplist_nr_pages > ihcp_max_sg_length) { EPRINTI("too many maplist pages!"); return -EIO; } /* * grind through the map list, extracting addresses and lengths for the sg list * don't forget the initial offset, and total length */ maplist = unit_p->maplist_p; map_length = unit_p->maplist_length; map_offset = unit_p->maplist_offset; map_nr_pages = unit_p->maplist_nr_pages; for (i = 0; i < map_nr_pages; i++) { temp_length = (PAGE_SIZE - map_offset) < map_length ? PAGE_SIZE - map_offset : map_length; /* * newer kernels with chained-scatterlist support don't * allow accessing the page element directly * * use macro to set page, offset, and length (in ihcp_var.h) * * this is what it used to look like: * * unit_p->sg_list_p[i].page = maplist[i]; * unit_p->sg_list_p[i].offset = map_offset; * unit_p->sg_list_p[i].length = temp_length; */ IHCP_SET_PAGE((unit_p->sg_list_p + i), maplist[i], temp_length, map_offset); DPRINTI("page_ptr = 0x%p, page_offset = 0x%x, page_len = 0x%x", maplist[i], map_offset, temp_length); map_length -= temp_length; map_offset = 0; } return 0; } /* * convert sg list to iopb list for plx dma logic */ static int ihcp_sg_list_to_iopb_list(struct ihcp_unit_t *unit_p, int instance) { int num_iopbs; int iopb_number; uint32_t temp_bus_addr; int temp_length; uint32_t next_iopb_add; uint32_t plx_direction; DPRINT("entering routine"); /* * make sure num_iopbs <= kio_pages (we should probably just trust the mapping code) */ num_iopbs = unit_p->nr_iopbs; if (num_iopbs > unit_p->maplist_nr_pages) { EPRINTI("num_iopbs > maplist_nr_pages!"); return -EIO; } plx_direction = unit_p->maplist_rw_mode == READ ? DMA_INPUT : 0; /* * now assemble the scatter list into a form that the PLX chip wants to see, and build iopb list * use IOPB macros to ease possible later mods to swizzle byte lanes */ for (iopb_number = 0; iopb_number < num_iopbs; iopb_number++) { temp_bus_addr = (uint32_t) sg_dma_address(&(unit_p->sg_list_p[iopb_number])); temp_length = sg_dma_len(&(unit_p->sg_list_p[iopb_number])); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_PCI_ADDRESS, temp_bus_addr); /* buf segment bus address */ IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_TRANSFER_SIZE, temp_length); /* buf segment size */ IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_LOCAL_ADDRESS, IHCP_32_BIT_DATA_OUT); /* data destination */ DPRINTI("iopb number 0x%x, buf seg addr = 0x%x, buf seg length = 0x%x", iopb_number, temp_bus_addr, temp_length); /* * the next descriptor pointer element of the iopb includes the location bit * (pci or local memory), the end of chain bit, the interrupt after terminal * countbit (not used), the direction of transfer bit (0 = output to device), * and the bus add of the next iopb * * if the iopb_number = num_iopbs -1, this is the last one so flag it, and * don't set a next iopb address */ if (iopb_number < (num_iopbs - 1)) { next_iopb_add = DMA_NEXT_ADDRESS_MASK & ((uint32_t)unit_p->dma_handle + ((iopb_number + 1) * IOPB_SIZE)); DPRINTI("not last iopb, next iopb add = 0x%x", next_iopb_add); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_NEXT_IOPB, DMA_CHAIN_IN_PCI_MEM | next_iopb_add | plx_direction); } else { DPRINTI("last iopb, setting end of chain bit"); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_NEXT_IOPB, DMA_CHAIN_IN_PCI_MEM | DMA_END_OF_CHAIN | plx_direction); } } return 0; } /* * set hardware byte order to board's current ordering mode */ static void ihcp_set_byte_order(struct ihcp_unit_t *unit_p) { if (unit_p->byte_order == HIGH_BYTE_FIRST) { IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) | BYTE_SWIZZLE)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) & ~USER_OUTPUT)); } else { IHCP_PUTL(IHCP_MODE, (IHCP_GETL(IHCP_MODE) & ~BYTE_SWIZZLE)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) | USER_OUTPUT)); } } /* * new-style init and cleanup routine invocations */ module_init(ihcp_init_module); module_exit(ihcp_cleanup_module); ./ihcp_install-2.60000755000076400007640000002643010630572300012312 0ustar wdwwdw#!/bin/bash # # ihcp_install-2.6 # Script to compile and load Tahoma Technology # hardcopy driver for Linux version 2.6. # Can also configure rc.local and /etc/ihcp for driver autoload on boot. # This code released under the GPL, and in the public domain # References to IKON left in place for compatibility and historical # reasons # # Tahoma Technology # (formerly Ikon Corporation) # 107 2nd Avenue North # Seattle, WA, USA 98109 # # 206.728.6465 # http://www.tahomatech.com # tahoma@tahomatech.com # ***************************************** # SEE BELOW FOR USER CONFIGURABLE VARIABLES # ***************************************** # general script variables IHCP_SCRIPT_NAME="ihcp_install-2.6" IHCP_SCRIPT_DATE="3 JUNE, 2007 08:07" IHCP_SCRIPT_DESC="Tahoma Technology (formerly Ikon Corporation) hardcopy driver installer" IHCP_LINUX_VERS="for Linux version 2.6.x including x86_64" IHCP_THISDIR=`pwd` # make and copy variables IHCP_MAKE=make IHCP_GREP=grep IHCP_SOURCE=ihcp_driver IHCP_CFLAGS_EXTRA= IHCP_DFLAGS= IHCP_MODULE=ihcp IHCP_KERNEL_VERSION=`uname -r` IHCP_KERNEL_SOURCE_DIR=/lib/modules/$IHCP_KERNEL_VERSION/build IHCP_MODULE_DIR=/lib/modules/$IHCP_KERNEL_VERSION/misc IHCP_TEST_DIR= # load (insmod) and dev node variables IHCP_INSMOD=/sbin/insmod IHCP_RMMOD=/sbin/rmmod IHCP_FFLAG= IHCP_INT_NODELIST="" IHCP_INT_NODE="" IHCP_TEMP=ihcp_temp IHCP_DRIVER=$IHCP_MODULE IHCP_NODENAME=$IHCP_MODULE IHCP_MINOR=0 IHCP_GROUP=bin IHCP_OWNER=bin IHCP_PERM=666 IHCP_INIT_SCRIPT="/etc/rc.d/rc.local" IHCP_INSTALL_SCRIPT_DIR=/etc/ihcp IHCP_INSTALL_SCRIPT_PERM=744 IHCP_INSTALL_SCRIPT_DIR_PERM=755 # *****USER CONFIGURABLE SCRIPT BEHAVIOR VARIABLES***** # these control variables have two or three options # those with YES|NO options force the associated # behavior -- those with an AUTO option allow the # script and/or the driver to autoconfigure the # associated behavior IHCP_USER_DMA=AUTO # NO|AUTO - dma direct from user buffer # AUTO = YES unless kernel configured for > 4G RAM # NO or > 4G RAM = dma from kernel copy buffer IHCP_ENB_DEBUG=NO # YES|NO - YES or cmd line option = debug mode IHCP_FORCE_LOAD=NO # YES|NO - force module load # *****USER CONFIGURABLE LOAD-TIME VARIABLES***** # the following will be passed to insmod and may # be edited to control driver and device configuration # IHCP_BOARDS determines the number of boards probed # for and device nodes created IHCP_BOARDS=4 #number of boards and dev nodes IHCP_MAX_PHYS_ORDER=4 #max dma xfer in "orders" of pages #also controls size of copy buffer (if not user dma) #larger user buffers will be done in max_phys chunks #max = PAGE_SIZE*(2**IHCP_MAX_PHYS_ORDER) #w/current 4K page size, 0=4K, 1=8K, 2=16K, 3=32K, 4=64K ... IHCP_VERS_SPEED=1 #versatec speed: 0=fastest, 3=slowest IHCP_CENT_SPEED=1 #centronics speed: 0=fastest, 3=slowest IHCP_MODE=1 #versatec mode: 1=plot, 0=print IHCP_DMA_TIME=1800 #dma timeout in seconds IHCP_FIFO_TIME=1800 #fifo & ready timeout in seconds IHCP_BYTE_ORDER=0 #byte endian mode, 0=low byte first, 1=high byte first echo # if no argument, print usage if [ -z $1 ] then echo "$IHCP_SCRIPT_NAME: $IHCP_SCRIPT_DATE" echo "$IHCP_SCRIPT_DESC" echo "$IHCP_LINUX_VERS" echo echo "usage: $IHCP_SCRIPT_NAME [compile|load|autoload|remove|all]" echo echo " compile: compiles driver and copies to module directory" echo " load: loads driver into running kernel and creates device node(s)" echo " autoload: copies this script to $IHCP_INSTALL_SCRIPT_DIR/" echo " adds entry to rc.local to autoload driver at boot time" echo " remove: unloads driver, deletes module, removes autoload entry," echo " script copy and directory, and device nodes" echo " all: remove, compile, load, and autoload" echo echo " compile and all may take an optional second argument: debug, which" echo " causes the driver to be compiled in debug mode (many printfs to log)" echo echo " some compile/install options are autodetected by this script and/or the" echo " driver code -- autodetected and hard coded options may be changed by" echo " editing this script" echo exit 0 fi # if arg #2 is debug, or debug enabled in script - set debug compile mode if [ $IHCP_ENB_DEBUG = YES ] then IHCP_DFLAGS=-DIHCP_DEBUG fi if ! [ -z $2 ] then if [ $2 = debug ] then IHCP_DFLAGS=-DIHCP_DEBUG else echo "***** unrecognized 2nd argument!" ./$IHCP_SCRIPT_NAME exit 1 fi fi # compile driver module and copy to module directory # first sort out the compile options if [ $1 = compile ] then echo "$IHCP_SCRIPT_NAME: command is $1 $2" echo "removing old $IHCP_MODULE.o .ko .mod.c .mod.o files (if any)" rm -f $IHCP_MODULE.o rm -f $IHCP_MODULE.ko rm -f $IHCP_MODULE.mod.c rm -f $IHCP_MODULE.mod.o # pass user dma/copy buffer mode to compiler if [ $IHCP_USER_DMA = AUTO ] then echo "auto dma/copy buffer mode" IHCP_CFLAGS_EXTRA="$IHCP_CFLAGS_EXTRA -DIHCP_DMA_MODE_AUTO" else echo "forcing copy buffer mode" IHCP_CFLAGS_EXTRA="$IHCP_CFLAGS_EXTRA -DIHCP_DMA_MODE_COPY" fi if [ -n "$IHCP_DFLAGS" ] then echo "debug compile enabled" fi echo "compiling $IHCP_SOURCE.c using kbuild make" if ! $IHCP_MAKE -C $IHCP_KERNEL_SOURCE_DIR SUBDIRS=$PWD EXTRA_CFLAGS="$IHCP_DFLAGS $IHCP_CFLAGS_EXTRA" modules then echo "***** make error!" exit 1 fi if ! [ -d $IHCP_MODULE_DIR ] then echo "creating $IHCP_MODULE_DIR" mkdir $IHCP_MODULE_DIR fi echo "copying $IHCP_MODULE.ko to $IHCP_MODULE_DIR" if ! cp $IHCP_MODULE.ko $IHCP_MODULE_DIR/ then echo "***** error copying $IHCP_MODULE.ko to $IHCP_MODULE_DIR!" exit 1 fi echo "compile and copy complete" exit 0 fi # [force] load module into kernel and create device nodes if [ $1 = load ] then echo "$IHCP_SCRIPT_NAME: command is $1" if [ $IHCP_FORCE_LOAD = YES ] then IHCP_FFLAG='-f' echo "forced load enabled" fi echo "loading module into kernel" if ! $IHCP_INSMOD $IHCP_FFLAG $IHCP_MODULE_DIR/$IHCP_MODULE.ko\ vers_speed_def=$IHCP_VERS_SPEED\ cent_speed_def=$IHCP_CENT_SPEED mode_def=$IHCP_MODE\ dma_time_def=$IHCP_DMA_TIME fifo_time_def=$IHCP_FIFO_TIME\ max_phys_order=$IHCP_MAX_PHYS_ORDER max_boards=$IHCP_BOARDS\ byte_order_def=$IHCP_BYTE_ORDER then echo "***** insmod error!" exit 1 fi echo "removing stale device nodes (if any)" rm -f /dev/$IHCP_NODENAME* echo "getting device major number from /proc/devices" IHCP_MAJOR=`cat /proc/devices | awk "\\$2==\"$IHCP_DRIVER\" {print \\$1}"` echo "getting node list from /proc/interrupts" IHCP_INT_NODELIST=`$IHCP_GREP -E -o "\<$IHCP_NODENAME[[:digit:]]+" /proc/interrupts` echo "creating device node(s)" while [ $IHCP_BOARDS -gt 0 ] do for IHCP_INT_NODE in $IHCP_INT_NODELIST do if [ "$IHCP_INT_NODE" = $IHCP_NODENAME$IHCP_MINOR ] then if ! mknod /dev/$IHCP_NODENAME$IHCP_MINOR c $IHCP_MAJOR $IHCP_MINOR then echo "***** mknod error!" exit 1 fi echo "created device node /dev/$IHCP_NODENAME$IHCP_MINOR" echo "setting device node ownership" chown $IHCP_OWNER /dev/$IHCP_NODENAME$IHCP_MINOR chgrp $IHCP_GROUP /dev/$IHCP_NODENAME$IHCP_MINOR chmod $IHCP_PERM /dev/$IHCP_NODENAME$IHCP_MINOR fi done IHCP_BOARDS=$( expr $IHCP_BOARDS - 1 ) IHCP_MINOR=$( expr $IHCP_MINOR + 1 ) done echo "driver loaded and device nodes created" echo "load complete" exit 0 fi # copy this script to script dir (/etc/ihcp, probably) # invoke it (load) from /etc/rc.d/rc.local if [ $1 = autoload ] then echo "$IHCP_SCRIPT_NAME: command is $1" echo "removing old $IHCP_SCRIPT_NAME (if present) from $IHCP_INSTALL_SCRIPT_DIR" if [ -d $IHCP_INSTALL_SCRIPT_DIR ] then rm -f $IHCP_INSTALL_SCRIPT_DIR/$IHCP_SCRIPT_NAME* fi echo "creating $IHCP_INSTALL_SCRIPT_DIR (if necessary) and setting permissions" if [ -d $IHCP_INSTALL_SCRIPT_DIR ] then echo "$IHCP_INSTALL_SCRIPT_DIR exists, leaving permissions unchanged" else if ! mkdir $IHCP_INSTALL_SCRIPT_DIR then echo "***** error creating $IHCP_INSTALL_SCRIPT_DIR!" exit 1 fi if ! chmod $IHCP_INSTALL_SCRIPT_DIR_PERM $IHCP_INSTALL_SCRIPT_DIR then echo "***** error setting permissions on $IHCP_INSTALL_SCRIPT_DIR!" exit 1 fi fi echo "copying $IHCP_SCRIPT_NAME to $IHCP_INSTALL_SCRIPT_DIR and setting permissions" if ! cp $IHCP_SCRIPT_NAME $IHCP_INSTALL_SCRIPT_DIR/ then echo "***** error copying $IHCP_SCRIPT_NAME to $IHCP_INSTALL_SCRIPT_DIR!" rmdir --ignore-fail-on-non-empty $IHCP_INSTALL_SCRIPT_DIR exit 1 fi if ! chmod $IHCP_INSTALL_SCRIPT_PERM $IHCP_INSTALL_SCRIPT_DIR/$IHCP_SCRIPT_NAME then echo "***** error setting permissions on $IHCP_SCRIPT_NAME!" exit 1 fi echo "removing old $IHCP_SCRIPT_NAME entry (if any) from $IHCP_INIT_SCRIPT" if ! sed /$IHCP_SCRIPT_NAME/d $IHCP_INIT_SCRIPT > ./$IHCP_TEMP then echo "***** sed error removing entry from $IHCP_INIT_SCRIPT!" exit 1 fi if ! cp ./$IHCP_TEMP $IHCP_INIT_SCRIPT then echo "***** error writing back $IHCP_INIT_SCRIPT!" exit 1 fi echo "adding $IHCP_SCRIPT_NAME to $IHCP_INIT_SCRIPT" if ! echo "if [ -x $IHCP_INSTALL_SCRIPT_DIR/$IHCP_SCRIPT_NAME ] ; then { date ; echo ; echo \"\$0: calling $IHCP_INSTALL_SCRIPT_DIR/$IHCP_SCRIPT_NAME load\" ; $IHCP_INSTALL_SCRIPT_DIR/$IHCP_SCRIPT_NAME load ; } > $IHCP_INSTALL_SCRIPT_DIR/$IHCP_SCRIPT_NAME.bootlog 2>&1 ; fi" >> $IHCP_INIT_SCRIPT then echo "***** error adding entry to $IHCP_INIT_SCRIPT!" exit 1 fi rm -f ./$IHCP_TEMP echo "$IHCP_SCRIPT_NAME added to $IHCP_INIT_SCRIPT" exit 0 fi # unload driver and clean up if [ $1 = remove ] then echo "$IHCP_SCRIPT_NAME: command is $1" echo "unloading module from kernel (if present)" $IHCP_RMMOD $IHCP_MODULE echo "removing device nodes" rm -f /dev/$IHCP_NODENAME* echo "deleting $IHCP_MODULE.ko module from $IHCP_MODULE_DIR" rm -f $IHCP_MODULE_DIR/$IHCP_MODULE.ko echo "removing $IHCP_MODULE_DIR (if present and empty)" rmdir --ignore-fail-on-non-empty $IHCP_MODULE_DIR echo "removing $IHCP_SCRIPT_NAME entry (if any) from $IHCP_INIT_SCRIPT" if ! sed /$IHCP_SCRIPT_NAME/d $IHCP_INIT_SCRIPT > ./$IHCP_TEMP then echo "***** sed error removing entry from $IHCP_INIT_SCRIPT!" exit 1 fi if ! cp ./$IHCP_TEMP $IHCP_INIT_SCRIPT then echo "***** error writing back $IHCP_INIT_SCRIPT!" exit 1 fi rm -f ./$IHCP_TEMP echo "deleting $IHCP_SCRIPT_NAME (if present) from $IHCP_INSTALL_SCRIPT_DIR" if ! rm -f $IHCP_INSTALL_SCRIPT_DIR/$IHCP_SCRIPT_NAME* then echo "***** error deleting $IHCP_INIT_SCRIPT from $IHCP_INSTALL_SCRIPT_DIR!" exit 1 fi echo "removing $IHCP_INSTALL_SCRIPT_DIR (if present and empty)" rmdir --ignore-fail-on-non-empty $IHCP_INSTALL_SCRIPT_DIR echo "remove complete" exit 0 fi # remove, compile, load, build nodes, and set autoload if [ $1 = all ] then echo "$IHCP_SCRIPT_NAME: command is $1 $2" if ! ./$IHCP_SCRIPT_NAME remove then echo "***** remove error!" exit 1 fi if ! ./$IHCP_SCRIPT_NAME compile $2 then echo "***** compile error!" exit 1 fi if ! ./$IHCP_SCRIPT_NAME load then echo "***** load error!" exit 1 fi if ! ./$IHCP_SCRIPT_NAME autoload then echo "***** autoload error!" exit 1 fi echo "" echo "remove, compile, load, and autoload complete" exit 0 fi # if unrecognized arg, bitch, and print usage echo "***** unrecognized 1st argument!" ./$IHCP_SCRIPT_NAME exit 1 ./ihcp_io.h0000644000076400007640000005517710526160331011206 0ustar wdwwdw/* * ihcp_io.h * public ioctl and default definition file for Linux 2.0.35 driver for PCI hardcopy boards * * (this file unchanged through 2.6.x versions of driver) * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * This code released under the GPL, and in the public domain * References to IKON left in place for compatibility and historical reasons */ #ifndef IHCP_IO_H #define IHCP_IO_H /* * the defines in this file are the "public" definitions used by the driver and the * calling program * * included are the ioctl command codes used by the code that calls the driver, and * some defaults that may be changed by editing this file and re compiling the driver * * this file is also used inside driver code !!! don't mess with this unless you mean it! */ #include #include /* the ioctl() function call looks like: * * ioctl(filedescriptor,command,argument) * * argument is used in some of the ihcp ioctl calls to provide values to * the driver ioctl routine, or return values to the calling program. In * one case - data out - it contains an array of charater data to be sent * to the attached device. the argument is restricted to a maximum of 255 * bytes - by unix, and by the driver. * * the following ioctl commands * are available to programs using the ihcp driver: * * *** legacy ioctl commands - originally for Sbus hardcopy cards *** * * IHCPIO_DEV_RESET reset attached device * IHCPIO_SET_CONFIG select device timing,burst, and busy mode w/ value in arg * IHCPIO_SET_DMATIME set dma timeout to arg seconds * IHCPIO_SET_FIFOTIME set fifo <1/2 full and fifo empty+device ready * timeout to arg seconds * IHCPIO_GET_REGS return board's registers in arg * IHCPIO_GET_STATUS return device status in arg * IHCPIO_GET_BOARD return board type in arg * IHCPIO_SET_VMODE set versatec print/plot/spp mode using value in arg * IHCPIO_SET_VMODEX same as VMODE, but clear ready sync ff - most plotters * will use VMODE - both for versatec only * IHCPIO_V_CMD send pulse command to plotter using value in arg * (versatec only) * IHCPIO_DATA_OUT send data bytes from arg array to device. byte count * in IHCPIO_COUNT_MASK portion of cmd * IHCPIO_RDY_WAIT wait for fifo empty and device ready * IHCPIO_HALF_WAIT wait for fifo less than 1/2 full * IHCPIO_STREAM_ON select data streaming mode (centronics only) * IHCPIO_STREAM_OFF clear data streaming mode * IHCPIO_GET_FLAGS returns unit_flags in arg * IHCPIO_GET_FIFO returns fifo status in arg * IHCPIO_MASTER_CLEAR resets board and clears fifo FACTORY USE ONLY! * IHCPIO_SOFT_ACK simulates ack sequence from device * * *** new ioctl commands for PCI hardcopy cards ** * * IHCPIO_AUTO_LTR_COUNT set automatic line terminate byte count (line length) to arg bytes * IHCPIO_AUTO_LTR_ON enable automatic line terminate * IHCPIO_AUTO_LTR_OFF disable auto line terminate * IHCPIO_DEV_AND_VEND_ID return board device and vendor ids in arg * IHCPIO_REVISION_ID return board revision level in arg * IHCPIO_LITTLE_ENDIAN set little endian mode - plotter data will go low byte first * IHCPIO_BIG_ENDIAN set big endian mode - plotter data will go high byte first * * ioctls to allow direct fiddling of mode and device control * * IHCPIO_DIRECT_MODE direct write arg to mode register * IHCPIO_DEVICE_CONTROL direct write arg to device control * IHCPIO_INTERFACE_STATUS direct read of interface status register returned in arg * IHCPIO_DEVICE_STATUS direct read of device status register returned in arg * IHCPIO_REVERSE_DATA read of reverse data register returned in arg * */ /* the ioctl command codes conform to the unix pattern: * * the top 3 bits of the 32 bit value indicate whether arguments * are to be copied in, copied out, both, or neither. * * 0x80000000 = copy in * 0x40000000 = copy out * 0x20000000 = no argument transfer * 0xC0000000 = copy in and out * * the number of bytes in the argument is encoded in the lower * 8 bits of the upper half of the u_int, and the actual command * is encoded in the lower half. a rather arbitrary character, * which is intended to identify the driver, is also encoded in * the lower half of the command. it becomes part of the command * value. * for all commands except DATA_OUT, the arg length is part of * the command value. * * WHEN USING THE IHCPIO_DATA_OUT IOCTL, * THE CALLING PROGRAM IS REQUIRED TO COMBINE THE ARGUMENT LENGTH * WITH THE IOCTL COMMAND AS FOLLOWS: * * cmd = IHCPIO_DATA_OUT | ( ( arg_length & 0xFF ) << 16 ) ; * * more accurately, for linux: * * cmd = IHCPIO_DATA_OUT | ((arg_length & _IOC_SIZEMASK) << _IOC_SIZESHIFT) * * commands which require arguments - in or out - will pass those * values as 32 bit unsigned integers. the only exception is the * DATA_OUT ioctl, which uses an array of up to 255 bytes as its * argument. * * the magic character that identifies this driver is hereby * (arbitrarily) chosen to be 'H'. * * the following ioctl commands are defined using pre-existing * ioctl command macros. the CMD_MASK and COUNT_MASK values * defined MUST match the usage in ioctl.h. refer to that include * file for further information. * * ADDITIONAL IOCTL COMMANDS AND ARGUMENTS ARE FOUND AT THE END OF THIS * FILE. THEY ARE INCLUDED FOR COMPATIBILITY WITH SUN AND VERSATEC * DRIVERS FOR THE 10088 VME BOARD, AND WILL HOPEFULLY BE COMPATIBLE * WITH OTHER MANUFACTURERS BOARDS AND DRIVERS (SHOULD THERE BE ANY!) * * the "magic character" for these commands is "v" */ /* * other versions of this driver used the entire lower 16 bits of the command to decode the specific * function requested. this includes both the ioctl type (magic letter) and number as defined * in linux/ioctl.h. we reconstruct the full function mask the hard way to try to protect against * the shifting sands of linux defines */ #define IHCPIO_CMD_MASK ((_IOC_TYPEMASK << _IOC_TYPESHIFT) | (_IOC_NRMASK << _IOC_NRSHIFT)) #define IHCPIO_COUNT_MASK IOCSIZE_MASK /* arg byte count here - IOCSIZE_MASK is already shifted left */ /* * THE SOLARIS 2.0 MACROS IN ioccom.h WANT THE 'MAGIC LETTER' QUOTED. * THE SOLARIS 1.X MACROS WANTED IT WITHOUT QUOTES. * LINUX WANTS QUOTES * * SOME VERSIONS OF ioccom.h DIDN'T INCLUDE _IORN AND _IOWN. SOL2 SEEMS TO HAVE THEM * linux/ioctl.h DOESN'T SEEM TO HAVE THEM * * we will use the lower-level _IOC macro (asm/ioctl.h) to define these since we need to * provide the size as an actual byte count, not as a type input to sizeof(), which is used * by the higher level (_IOR, _IOW) macros */ #define _IORN(type, nr, size) _IOC(_IOC_READ, (type), (nr), (size)) #define _IOWN(type, nr, size) _IOC(_IOC_WRITE, (type), (nr), (size)) #define IHCPIO_DEV_RESET _IO('H',0) /* reset attached device */ /* sets & resets latched reset */ #define IHCPIO_SET_CONFIG _IOW('H',1,__u32) /* arg bits set configuration */ /* * the config bit patterns use those defined for the Sbus card - even * though they don't match the PCI card's bit posistions. this is done * to try to preserve compatibility between applications * * the first version of this driver doesn't try to support all the possible * modes available in the PCI card. * */ /* *** legacy bits **** */ #define IHCPIO_SPEED0 0x00 /* slowest device speed */ #define IHCPIO_SPEED1 0x04 #define IHCPIO_SPEED2 0x08 #define IHCPIO_SPEED3 0x0C /*fastest device timing */ #define IHCPIO_IGNORE_BUSY 0x80 /* ignore centr busy */ /* *** new for PCI *** */ #define IHCPIO_BUSY_HANDSHAKE 0x100 /* use BUSY instead of ACK */ #define IHCPIO_4_EDGE 0x200 /* use four edge handshake */ #define IHCPIO_V_BURST 0x400 /* select synch burst mode only */ /* available on some new v-tech */ /* and requires pld218B or later */ #define IHCPIO_SET_DMATIME _IOW('H',2,__u32) /* set dma timeout to arg secs */ #define IHCPIO_SET_FIFOTIME _IOW('H',3,__u32) /* set wait for <1/2 full or */ /* empty and dev rdy timeout */ /* to arg seconds */ /* * return array size used here and in ioctl code * _IORN takes the direct byte count, not a type to be * passed to the sizeof() operator, as is done with * the other _IOx macros */ #define IHCP_RETURN_ARRAY_SIZE 19 /* in long words!! */ #define IHCPIO_GET_REGS _IORN('H',4,IHCP_RETURN_ARRAY_SIZE * 4) /* puts all regs in arg array */ /* * returns all "IHCP" registers and some of the "PLX" registers. * also returns the device IDs and revision level. * PLX registers are all 32 bits wide. IHCP registers are 8 * bits wide. each is returned in a 32 bit longword as shown below. * the DMA registers returned are either channel 0 or 1, depending * on which is used in the particular revision of the board. * * * (__u32 larg[19]) * * larg[0] = device ID (high 16 bits) & vendor id (low 16 bits) * larg[1] = revision ID * larg[2] = PLX interrupt control/status * larg[3] = PLX EEPROM control and user bits * larg[4] = PLX DMA mode * larg[5] = PLX DMA PCI address * larg[6] = PLX DMA local address * larg[7] = PLX DMA transfer count * larg[8] = PLX DMA descriptor pointer * larg[9] = PLX DMA command/status register * larg[10]= interrupt mask * larg[11]= mode * larg[12]= device control * larg[13]= interface control * larg[14]= interface status * larg[15]= device status * larg[16]= reverse data * larg[17]= auto ltr count low * larg[18]= auto ltr count high * */ #define IHCPIO_GET_STATUS _IOR('H',5,__u32) /* returns FORMATTED device */ /* status in arg */ #define IHCPIO_DEV_RDY 0x00000010 /* v-rdy or centr ack asserted */ #define IHCPIO_DEV_BUSY 0x00000008 /* centronics asserting busy */ #define IHCPIO_DEV_FAULT 0x00000004 /* centronics asserting fault */ #define IHCPIO_DEV_POUT 0x00000002 /* v or c no paper */ #define IHCPIO_DEV_SEL 0x00000001 /* v online or c selected */ #define IHCPIO_GET_BOARD _IOR('H',6,__u32) /* gets board strapping - sbus compatibility */ /* strapping bits returned in arg */ #define IHCPIO_VERS_DIFF 0x000000A0 /* versatec differential */ #define IHCPIO_VERS_TTL 0x00000060 /* versatec ttl */ #define IHCPIO_CENT 0x000000C0 /* centronics */ #define IHCPIO_SET_VMODE _IOW('H',7,__u32) /* sets print/plot, spp as selected by arg */ #define IHCPIO_SET_VMODEX _IOW('H',8,__u32) /* same as above for PCI boards */ #define IHCPIO_V_SPP 0x00000002 /* sets spp w/MODE(X) */ #define IHCPIO_V_PLOT 0x00000001 /* sets plot mode w/MODE(X) */ #define IHCPIO_V_NPRINT 0x00000000 /* 0 = normal print mode */ #define IHCPIO_V_CMD _IOW('H',9,__u32) /* pulse commands to versatec selected by arg */ #define IHCPIO_VLTR 0x00000000 /* remote line terminate */ #define IHCPIO_VEOT 0x00000001 /* remote eot */ #define IHCPIO_VFED 0x00000002 /* remote form feed */ #define IHCPIO_VCLR 0x00000003 /* remote buffer clear */ #define IHCPIO_DATA_OUT _IOWN('H',10,0) /* CALLING PGM WILL AND IN THE */ /* ACTUAL CHARACTER COUNT - DONE */ /* THIS WAY SO COUNT CAN BE A */ /* VARAIBLE, NOT CONSTANT !!! */ /* see ioctl description above */ /* sends char array in arg to */ /* fifo if room. if not - return */ /* EINVAL. will flag error if */ /* caller tries to send more */ /* 255 bytes, or if fifo becomes */ /* full during transfers. */ #define IHCPIO_RDY_WAIT _IO('H',11) /* waits for fifo empty and */ /* device ready */ #define IHCPIO_HALF_WAIT _IO('H',12) /* wait for fifo 64K max w/4K pages */ #define MAX_BOARDS_DEF 2 /* default to two boards, to save a little kernel memory */ #endif /* IHCP_IO_H */ ./ihcp_reg.h0000644000076400007640000005446510526160331011353 0ustar wdwwdw/* * ihcp_reg.h * Register definition file for Linux driver for PCI hardcopy boards * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * This code released under the GPL, and in the public domain * References to IKON left in place for compatibility and historical reasons */ #ifndef IHCP_REG_H #define IHCP_REG_H /* * * Tahoma Technology's PCI hardcpopy boards include the following: * * Model 10115 full size card supporting Versatec TTL and Differential and Centronics * Model 10117 half size card supporting Versatec Differential and Centronics * * The boards share a common register set, and operate under * the same software driver. Not all functions are supported by all * boards - the software will check to make sure that a requested operation * is supported for that particular board. * * In particular, the 10117 does not have local memory for dma chain lists, and may in some * versions use DMA channel 1, rather than dma channel 0 * * All boards use some version of PLX technology's 9060 or 9080 PCI interface chip. Various * version of this chip determine the exact DMA setup requirements for the board. Some versions * support chaining DMA from local memory only, some support chaining lists in PCI memory, * and some implement DMA channel 1 only (channel 0 is used in most versions of the board). * Some versions of the chip require that we access the dma registers via a window in * the ihcp address range, and some require direct access via the plx address range. * * * There are three address ranges supported (and required) by the hardcopy boards: * configuration space, PLX register space, and IHCP register space. The board's registers * are accesses through various ddi_... functions, which take an address as one of the arguments. * We will access the registers using defined offsets from the appropriate address range * base addresses. Macros will be used to shorten the typing task somewhat. * */ /* * the configuration space offsets and bit definitions are * provided in pci.h */ /* * plx run-time register offsets - when accessed directly via the * plx address range */ #define PLX_ENDIAN_REG 0x0C /* endian control - actually a local config reg - 9080 ONLY */ #define PLX_ADD_0_ROM_DESC 0x18 /* add space 0/rom descriptor register */ #define PLX_MBX_0 0x40 /* mailbox registers */ #define PLX_MBX_1 0x44 #define PLX_MBX_2 0x48 #define PLX_MBX_3 0x4C #define PLX_MBX_4 0x50 #define PLX_MBX_5 0x54 #define PLX_MBX_6 0x58 #define PLX_MBX_7 0x5C #define PLX_PTOL_BELL 0x60 /* doorbell regsiters */ #define PLX_LTOP_BELL 0x64 #define PLX_INT_CSTAT 0x68 /* interrupt control/status regsiter */ #define PLX_EEPROM_USER 0x6C /* eeprom control and user bits */ #define PLX_DMA_MODE_0 0x80 /* dma channel 0 mode register */ #define PLX_DMA_PCI_ADD_0 0x84 /* pci address for dma transfer */ #define PLX_DMA_LOC_ADD_0 0x88 /* local address for dma transfer */ #define PLX_DMA_COUNT_0 0x8C /* dma transfer count in bytes */ #define PLX_DMA_DESC_PTR_0 0x90 /* dma descriptor pointer */ #define PLX_DMA_MODE_1 0x94 /* dma channel 1 mode register */ #define PLX_DMA_PCI_ADD_1 0x98 /* pci address for dma transfer */ #define PLX_DMA_LOC_ADD_1 0x9C /* local address for dma transfer */ #define PLX_DMA_COUNT_1 0xA0 /* dma transfer count in bytes */ #define PLX_DMA_DESC_PTR_1 0xA4 /* dma descriptor pointer */ #define PLX_DMA_CMD_STAT_BOTH 0xA8 /* dma command/status for both channels combined */ #define PLX_DMA_CMD_STAT_0 0xA8 /* dma command/status for channel 0 */ #define PLX_DMA_CMD_STAT_1 0xA9 /* dma command/status for channel 1 */ #define PLX_DMA_ARB_REG 0xAC /* local/dma arbitration register */ #define PLX_DMA_THRESH 0xB0 /* DMA threshold register */ /* * plx run-time register offsets when accesses via window * in ihcp address space */ #define IPLX_ENDIAN_REG 0x8C /* endian control - actually a local config reg - 9080 ONLY */ #define IPLX_ADD_0_ROM_DESC 0x98 /* add space 0/rom descriptor register */ #define IPLX_MBX_0 0xC0 /* mailbox registers */ #define IPLX_MBX_1 0xC4 #define IPLX_MBX_2 0xC8 #define IPLX_MBX_3 0xCC #define IPLX_MBX_4 0xD0 #define IPLX_MBX_5 0xD4 #define IPLX_MBX_6 0xD8 #define IPLX_MBX_7 0xDC #define IPLX_PTOL_BELL 0xE0 /* doorbell regsiters */ #define IPLX_LTOP_BELL 0xE4 #define IPLX_INT_CSTAT 0xE8 /* interrupt control/status regsiter */ #define IPLX_EEPROM_USER 0xEC /* eeprom control and user bits */ #define IPLX_DMA_MODE_0 0x100 /* dma channel 0 mode register */ #define IPLX_DMA_PCI_ADD_0 0x104 /* pci address for dma transfer */ #define IPLX_DMA_LOC_ADD_0 0x108 /* local address for dma transfer */ #define IPLX_DMA_COUNT_0 0x10C /* dma transfer count in bytes */ #define IPLX_DMA_DESC_PTR_0 0x110 /* dma descriptor pointer */ #define IPLX_DMA_MODE_1 0x114 /* dma channel 1 mode register */ #define IPLX_DMA_PCI_ADD_1 0x118 /* pci address for dma transfer */ #define IPLX_DMA_LOC_ADD_1 0x11C /* local address for dma transfer */ #define IPLX_DMA_COUNT_1 0x120 /* dma transfer count in bytes */ #define IPLX_DMA_DESC_PTR_1 0x124 /* dma descriptor pointer */ #define IPLX_DMA_CMD_STAT 0x128 /* dma command/status for both channels */ #define IPLX_DMA_ARB_REG 0x12C /* local/dma arbitration register */ #define IPLX_DMA_THRESH 0x130 /* DMA threshold register */ /* * bit definitions for the PLX run-time registers */ /* * endian control register - 9080 ONLY */ #define CONFIG_BIG_ENDIAN 0x00000001 /* causes local accesses to config space to be big endian - NOT USED */ #define MASTER_BIG_ENDIAN 0x00000002 /* direct master big endian - NOT USED */ #define SLAVE_BIG_ENDIAN 0x00000004 /* direct slave access to ihcp space big endian */ #define SLAVE_ROM_BIG_ENDIAN 0x00000008 /* direct slave accesses to rom space big endian - NOT USED */ #define BYTE_LANE_ENDIAN_MODE 0x00000010 /* causes upper word or byte to be used for access to 16 or 8 bit local bus */ #define SLAVE_1_BIG_ENDIAN 0x00000020 /* direct slave accesses to add space 1 big endian - NOT USED */ #define DMA_1_BIG_ENDIAN 0x00000040 /* dma channel 1 accesses big endian */ #define DMA_0_BIG_ENDIAN 0x00000080 /* dma channel 0 accesses big endian */ /* * address space 0/rom descriptor register * * for now, we only define the one bit we need */ #define ADD_0_PREFETCH_DISABLE 0x00000100 /* disable any prefetch reads to address space 0 */ /* * interrupt control/status register */ #define LSERR_ABORT_INT_ENABLE 0x00000001 /* enables local interrupt on target or master abort */ #define LSERR_PARITY_INT_ENABLE 0x00000002 /* enable local interrupt on PCI parity error */ #define PCI_SERR 0x00000004 /* forces assertion of PCI SERR# signal */ #define MAILBOX_INT_ENABLE 0x00000008 /* enables local interrupt on mailbox write from PCI 9080 ONLY */ #define DMA_0_INTERRUPT_SELECT 0x00000010 /* 1 causes dma 0 interrupt to assert PCI interrupt */ /* 0 causes dma 0 interrupt to assert local interrupt */ /* 9080 ONLY for other chips loop local out to local in on board */ #define DMA_1_INTERRUPT_SELECT 0x00000020 /* same as above - for dma channel 1 */ #define PCI_INTERRUPT_ENABLE 0x00000100 /* enables pci interrupts */ #define PCI_DB_INT_ENABLE 0x00000200 /* enables pci doorbell interrupts w/pci int enable */ #define PCI_ABORT_INT_ENABLE 0x00000400 /* enables pci interrupt on master or target abort w/pci int enb */ #define PCI_LOCAL_INT_ENABLE 0x00000800 /* enbles local interrupt to cause pci interrupt w/pci int enb */ #define RETRY_ABORT_ENABLE 0x00001000 /* 256 consecutive master retries will cause target abort */ #define PCI_DB_INTERRUPT 0x00002000 /* 1 indicates pci doorbell interrupt is active */ #define PCI_ABORT_INTERRUPT 0x00004000 /* 1 indicates pci abort interrupt is active */ #define LOCAL_INTERRUPT 0x00008000 /* 1 indicates local interrupt is active */ #define LOCAL_INT_OUT_ENABLE 0x00010000 /* enables local interrupt output */ #define LOCAL_DOORBELL_INT_ENB 0x00020000 /* enables local doorbell interrupts w/local int enb */ #define LOCAL_DMA_0_INT_ENABLE 0x00040000 /* enables dma chan 0 local interrupt w/local int enb */ #define LOCAL_DMA_1_INT_ENABLE 0x00080000 /* enables dma chan 1 local interrupt w/local int enb */ #define LOCAL_DB_INTERRRUPT 0x00100000 /* 1 indicates local doorbell interrupt is active */ #define DMA_0_INTERRUPT 0x00200000 /* 1 indicates dma channel 0 interrupt is active */ #define DMA_1_INTERRUPT 0x00400000 /* 1 indicates dma channel 1 interrupt is active */ #define BIST_INTERRUPT 0x00800000 /* 1 indicates that bilt in self test int is active */ #define DIRECT_MASTER_ABORT 0x01000000 /* 1 indicates abort during direct master xfer */ #define DMA_0_ABORT 0x02000000 /* 1 indicates abort during dma chan 0 xfer */ #define DMA_1_ABORT 0x04000000 /* 1 indicates abort during dma chan 1 xfer */ #define RETRY_ABORT 0x08000000 /* 1 indicates abort after 256 retries */ #define MBX_0_FLAG 0x10000000 /* 1 indicates PCI wrote to mailbox 0 9080 ONLY */ #define MBX_1_FLAG 0x20000000 /* 1 indicates PCI wrote to mailbox 1 9080 ONLY */ #define MBX_2_FLAG 0x40000000 /* 1 indicates PCI wrote to mailbox 2 9080 ONLY */ #define MBX_3_FLAG 0x80000000 /* 1 indicates PCI wrote to mailbox 3 9080 ONLY */ /* * eeprom control, pci command codes, user bits, init control */ #define DMA_READ_CMD_MASK 0x0000000F /* masks bits used as pci command during dma read */ #define DMA_READ_CMD_DEF 0x0000000E /* power up default for dma reads */ #define DMA_WRITE_CMD_MASK 0x000000F0 /* masks bits used as pci command during dma write */ #define DMA_WRITE_CMD_DEF 0x00000070 /* power up default for dma writes */ #define MASTER_READ_CMD_MASK 0x00000F00 /* masks bits used as pci command during direct master read */ #define MASTER_READ_CMD_DEF 0x00000600 /* power up default for direct master reads */ #define MASTER_WRITE_CMD_MASK 0x0000F000 /* masks bits used as pci command during direct master write */ #define MASTER_WRITE_CMD_DEF 0x00007000 /* power up default for direct master writes */ #define USER_OUTPUT 0x00010000 /* user output bit */ #define USER_INPUT 0x00020000 /* user input bit */ #define EEPROM_CLOCK 0x01000000 /* toggling this bit generates an eeprom clock */ #define EEPROM_CHIP_SELECT 0x02000000 /* drives the eeprom chip select */ #define EEPROM_WRITE_BIT 0x04000000 /* write bit to eeprom */ #define EEPROM_READ_BIT 0x08000000 /* read bit from eeprom */ #define EEPROM_PRESENT 0x10000000 /* 1 indicates that an eeprom is present */ #define CONFIGURATION_RELOAD 0x20000000 /* 0-to-1 causes reload of configuration registers from eeprom */ #define PLX_SOFT_RESET 0x40000000 /* holds plx chip reset and aserts local reset out (big hammer!) */ #define LOCAL_INIT_DONE 0x80000000 /* local init done - since no local master - this is held at 1 by #NB pin */ /* * dma channels 0 and 1 mode register bits */ #define DMA_BUS_WIDTH_MASK 0x00000003 /* mask for dma local bus width bits */ #define DMA_BUS_8_BIT 0x00000000 /* 8 bit dma bus */ #define DMA_BUS_16_bIT 0x00000001 /* 16 bit dma bus */ #define DMA_BUS_32_BIT 0x00000002 /* 32 bit dma bus */ #define DMA_WAIT_STATE_MASK 0x0000003C /* mask for local bus wait state count */ #define DMA_WAIT_1 0x00000004 /* one wait state used for current boards */ #define DMA_READY_IN_ENABLE 0x00000040 /* enable ready input during dma */ #define DMA_BTERM_IN_ENABLE 0x00000080 /* enable bterm input during dma */ #define DMA_BURST_ENABLE 0x00000100 /* enable local bursting during dma */ #define DMA_CHAIN_ENABLE 0x00000200 /* enable dma chaining */ #define DMA_DONE_INTERRUPT_ENB 0x00000400 /* enable dma done interrupt */ #define DMA_LOCAL_ADD_HOLD 0x00000800 /* 0 causes local add to increment during dma */ #define DMA_DEMAND_MODE 0x00001000 /* enables demand mode dma (DREQ# input) */ #define DMA_WRITE_INVALID 0x00002000 /* perform write and invalidate cycles 9080 ONLY */ #define DMA_EOT_ENABLE 0x00004000 /* enable eot input 9060SD and 9080 only */ #define DMA_STOP_MODE 0x00008000 /* 0=blast terminates xfer, 1=eot on, */ /* or dreq off in demand mode terminates 9060SD and 9080 ONLY */ #define DMA_CLEAR_COUNT_MODE 0x00010000 /* causes byte count in each descriptor to be zeroed when done 9080 ONLY */ /* * dma pci address registers, local address registers, and * transfer size registers have no special control bits * * transfer count register is limited to lower 23 bits * =8Mbyte max xfer per chain list element */ #define DMA_XFER_COUNT_MASK 0x007fffff /* * dma channels 0 and 1 descriptor pointer register bits */ #define DMA_CHAIN_IN_PCI_MEM 0x00000001 /* if set, dma chain list in pci memory, otherwise local memory */ /* some boards do not have local memory */ #define DMA_END_OF_CHAIN 0x00000002 /* if set, this chain element is last in list */ #define DMA_LINK_INTERRUPT 0x00000004 /* if set, interrupt when this element's count is exhausted */ #define DMA_INPUT 0x00000008 /* if set, direction of xfer is local to PCI */ #define DMA_NEXT_ADDRESS_MASK 0xfffffff0 /* bit mask for address of next quad word list element */ /* * dma channels 0 and 1 have identical BYTE wide command/status registers * which may be accessed at separate byte addresses, or as a single 16 or 32 * bit register * * dma channel 0 is at the lower offset, channel 1 is the next higher byte offset */ #define DMA_ENABLE 0x01 /* enables the dma channel when 1 - disables (pauses) when 0 */ #define DMA_START 0x02 /* 1 starts the dma transfer if enable true */ #define DMA_ABORT 0x04 /* 1 aborts the transfer, leaves enable true */ #define DMA_CLEAR_INTERRUPT 0x08 /* 1 clears the dma interrupt */ #define DMA_DONE 0x10 /* 1 indicates that the dma transfer is complete */ /* * dma arbitration and threshold registers left at * default of 0 for current board implementations */ /* * ichp (ikon logic portion of board) register offsets * registers are byte wide in low byte of 32 bit longword * except 32 bit data out register which is 32 bits wide * * all ihcp registers may be accessed as 32 bit values */ #define IHCP_INTERRUPT_MASK 0x00 /* interrupt mask for ikon portion of logic */ #define IHCP_MODE 0x04 /* mode control for ikon logic */ #define IHCP_DEVICE_CONTROL 0x08 /* device control register */ #define IHCP_INTERFACE_CONTROL 0x0C /* interface control register */ #define IHCP_INTERFACE_STATUS 0x10 /* interface status register */ #define IHCP_DEVICE_STATUS 0x14 /* device status regsiter */ #define IHCP_REVERSE_DATA 0x18 /* diagnostic and 1284 reverse data register */ #define IHCP_RESERVED 0x1C /* unused - reserved */ #define IHCP_AUTO_LTR_LOW 0x20 /* low byte of auto-ltr byte count register */ #define IHCP_AUTO_LTR_HIGH 0x30 /* high byte of auto-ltr byte count register */ #define IHCP_8_BIT_DATA_OUT 0x40 /* single data byte write to fifo */ #define IHCP_COMMAND_OUT 0x48 /* single command byte write to fifo */ #define IHCP_32_BIT_DATA_OUT 0x50 /* quad data byte write to fifo */ #define IHCP_PLX_RUNTIME_BASE 0x80 /* base of plx window in ihcp address range */ #define IHCP_MEMORY_BASE 0x140 /* base of local memory */ #define IHCP_FIFO_BASE 0x1000 /* base of fifo data burst range */ /* * bit definitions for the ikon (ihcp) regsiters */ /* * interrupt mask register bits */ #define FAULT_INT_ENB 0x01 /* enable centronics fault to cause interrupt */ #define OFFLINE_INT_ENB 0x02 /* enable not selected or offline to interrupt */ #define NOPAPER_INT_ENB 0x04 /* enable paper out to interrupt */ #define FIFO_NOT_FULL_INT_ENB 0x10 /* fifo not full interrupt */ #define FIFO_NOT_HALF_INT_ENB 0x20 /* interrupt if fifo not half full */ #define FIFO_EMPTY_INT_ENB 0x40 /* interrupt if fifo empty */ #define DEV_AND_INT_RDY_INT_ENB 0x80 /* interrupt if device and interface ready */ /* * ihcp mode register bit definitions */ #define SPEED_MASK 0x03 /* mask for handshake speed bits */ #define SPEED_0 0x00 /* fastest speed */ #define SPEED_1 0x01 #define SPEED_2 0x02 #define SPEED_3 0x03 #define BYTE_SWIZZLE 0x04 /* used with plx user output bit to select high */ /* byte of fifo to be output first */ #define V_BURST 0x08 /* synch burst mode for some Versatec machines */ /* requires pld218B or later */ #define IGNORE_BUSY 0x10 /* ignore centronics busy input */ #define BUSY_NOT_ACK 0x20 /* use busy for handshake instead of ack */ #define REVERSE_DATA_ENB 0x40 /* set data outputs to high Z */ #define FOUR_EDGE_HANDSHAKE 0x80 /* use ieee 1284 style 4 edge handshake */ /* * device control register bit definitions * * bits are latches - must be set to 0 after use */ #define ASSERT_INIT 0x01 /* assert nINIT to centronics device */ #define UNASSERT_SEL_IN 0x02 /* turns off sel in to centronics device */ #define ASSERT_AUTO_FD 0x04 /* asserts nAUTO FD to centronics device */ #define ASSERT_STROBE 0x08 /* asserts strobe to centronics device */ #define DISABLE_STROBE 0x20 /* disables strobe to device (data will still */ /* be pulled from fifo, but no strobe issued) */ #define DISABLE_READY 0x40 /* forces device ready status false */ #define FORCE_READY 0x80 /* forces device ready status true */ /* * interface control register bit definitions * * bits are latches - must be set to 0 after use */ #define RESET_DEVICE 0x01 /* 1 holds device reset input true */ #define CLEAR_INTERRUPT_FLAG 0x20 /* 1 clears ichp interrupt flag */ #define SOFTWARE_ACK 0x40 /* simulates ack pulse or ready sequence from device */ #define MASTER_CLEAR 0x80 /* master clear of ihcp logic */ /* * interface status register bit definitions */ #define DATA_PATH_8_BIT 0x01 /* set if board is straped for 8 bit data path */ #define PRINT_MODE_OUTPUT 0x02 /* follows state of print mode output AT OUTPUT */ #define FIFO_NOT_FULL 0x04 /* 1 if fifo not full */ #define FIFO_NOT_HALF_FULL 0x08 /* 1 if fifo not half full */ #define FIFO_EMPTY 0x10 /* 1 if fifo empty */ #define INTERRUPT_FLAG 0x20 /* 1 if ihcp logic interrupting */ #define DEVICE_READY 0x40 /* 1 if device ready */ #define DEVICE_AND_INT_READY 0x80 /* 1 if device and interface ready */ /* * device status register bit definitions */ #define FAULT 0x01 /* 1 if fault input is asserted */ #define ONLINE 0x02 /* 1 if device online */ #define PAPER_OUT 0x04 /* 1 if paper out */ #define CENTRONICS_BUSY 0x08 /* 1 if busy input asserted */ #define VERSATEC_READY 0x10 /* 1 if versatec ready input asserted */ #define INTERFACE_STRAP_MASK 0xE0 /* bits that indicate type of interface selected */ /* used internal to the driver - driver also uses */ /* equivalent defines in ..io.h for flag bits, since */ /* flag bits are visible to the outside world via ioctl */ #define VERSATEC_TTL 0x60 /* versatec ttl selected */ #define VERSATEC_DIFFERENTIAL 0xA0 /* versatec differential selected */ #define CENTRONICS 0xC0 /* centronics selected */ /* * command out register bit definitions */ #define VERSATEC_CLEAR 0x41 /* pulse versatec clear output */ #define VERSATEC_FORM_FEED 0x42 /* pulse form feed output */ #define VERSATEC_EOT 0x44 /* pulse e-o-t */ #define VERSATEC_LINE_TERM 0x48 /* pulse line terminate */ #define SET_VERSATEC_MODE 0x20 /* sets mode as determined by versatec mode bits */ #define NORM_PRINT_MODE 0x00 /* normal print mode bits */ #define PLOT_MODE 0x01 /* plot mode bits */ #define SPP_MODE 0x02 /* spp mode bits */ #define SET_IO_MODE 0x00 /* sets mode as determined by i/o mode bits */ #define AUTO_LTR_MODE 0x10 /* auto line terminate mode bits */ #define DATA_STREAMING_MODE 0x01 /* data streaming mode bits */ /* * defines for/from eeprom */ #define IKON_VENDOR_ID 0x11d5 /* our pci vendor id */ #define IHCP_DEVICE_ID_LIST 0x0115,0x0117 /* supported board IDs */ #define PLX_REG_SIZE 0x100 /* size of plx 9080 register set */ #define IHCP_REG_SIZE 0X2000 /* size of ikon register set */ /* * mask for revision id portion of class code & rev id config longword */ #define REV_ID_MASK 0xFF /* * we are 32 bit DMA capable */ #define IHCP_DMA_MASK 0xffffffff /* * define size of i/o parameter block (link of dma chain list) * and the offsets of elements of the block from the base address */ #define IOPB_SIZE 0x10 /* 16 bytes per block */ #define IOPB_PCI_ADDRESS 0x00 /* starting pci address of user buffer block */ #define IOPB_LOCAL_ADDRESS 0x04 /* starting local address */ #define IOPB_TRANSFER_SIZE 0x08 /* number of bytes in this block */ #define IOPB_NEXT_IOPB 0x0c /* physical address of next iopb in list */ #endif /* IHPC_REG_H */ ./ihcp_var.h0000644000076400007640000002075011057015536011362 0ustar wdwwdw/* * ihcp_var.h * structure and "software" macro definition file for Linux 2.6.x driver for * PCI hardcopy boards * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * This code released under the GPL, and in the public domain * References to IKON left in place for compatibility and historical reasons */ #ifndef IHCP_VAR_H #define IHCP_VAR_H /* * nasty version detection - 2.6.18 and later, UTS_RELEASE is in utsrelease.h * not version.h so include utsrelease.h */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) #include #endif /* * conditional variable and mutex typedefs */ typedef struct { spinlock_t spin; long flags; } ihcp_mutex_t; typedef struct { wait_queue_head_t queue; } ihcp_cond_t; enum { IHCP_COND_WAIT_SUCCESS, IHCP_COND_WAIT_SIGNAL, IHCP_COND_WAIT_TIMEOUT }; /* * unit structure for boards */ struct ihcp_unit_t { u_int instance; /* equivalent to minor number & board number */ ihcp_mutex_t mutex; /* used w/cond var to sleep (SMP compatibility) */ ihcp_cond_t cond; /* cond var used w/mutex */ volatile int sleeping; /* 1 if sleeping (helps detect timeout) */ struct pci_dev *pci_dev_p; /* handle for this board's pci dev structure */ void *plx_page_base_p; /* virtual address of plx reg page base */ void *ihcp_page_base_p; /* virtual address of ihcp reg page base */ void *plx_base_p; /* virtual address of plx regs */ void *ihcp_base_p; /* virtual address of ihcp regs */ void *iopb_base_p; /* virtual add of start of io parameter blocks */ int nr_iopbs; /* number of iopbs */ struct scatterlist *sg_list_p; /* points to scatterlist (for DMA mapping) */ dma_addr_t dma_handle; /* contains bus add of start of iopbs */ struct page **maplist_p; /* ptr to array of ptrs to page structs */ char *maplist_user_buf_p; /* ptr to current user buffer (or portion of) */ int maplist_nr_pages; /* number of pages in maplist */ int maplist_offset; /* offset from base of buffer */ int maplist_length; /* number of bytes in buffer */ int maplist_rw_mode; /* READ or WRITE */ char *dma_copy_buf_p; /* ptr to copy buffer, or NULL */ volatile u_int unit_open; /* 1 = open */ spinlock_t open_lock; /* protect open flag from contention */ volatile u_int unit_attached; /* 1 = attached */ u_int mode; /* copy of print/plot mode */ u_int data_stream_mode; /* copy of data streaming mode */ u_int byte_order; /* big or little endian data flag */ u32 unit_flags; /* our flag bits here */ u_int dma_time; /* dma timeout # in ticks */ u_int fifo_time; /* empty and <1/2 full # */ u_int dma_time_def; /* per unit timeout default */ u_int fifo_time_def; /* per unit timeout default */ u32 vers_speed_def; /* per unit versatec speed default */ u32 cent_speed_def; /* per unit centronics speed default */ u32 mode_def; /* per unit mode default */ u32 dev_and_vendor_id; /* device and vendor id from config regs */ u32 revision_id; /* revision id from config regs */ u_char int_level; /* int level read from config register */ u_char pci_dev_int_level; /* (possibly) mapped int level */ char minor_node_name[16]; /* will contain this board's node name */ }; /* * "software" macro definitions */ /* * debug print macros - expand to nothing unless IDR_DEBUG is defined * DPRINT: print module and subroutine name and arg string + args w/a \n * DPRINTI: same, but include instance number * DPRINTC: continuation line same, but starts w/8 spaces and no instance */ #ifdef IHCP_DEBUG #define DPRINT(format,args...) printk(KERN_DEBUG "ihcp: %s: " format "\n",\ __FUNCTION__ , ##args) #define DPRINTI(format,args...) printk(KERN_DEBUG "ihcp: %s: instance: %d: " format "\n",\ __FUNCTION__, instance , ##args) #else #define DPRINT(format,args...) #define DPRINTI(format,args...) #endif /* * info, error and warning printks like the above */ #define IPRINT(format,args...) printk(KERN_INFO "ihcp: %s: " format "\n",\ __FUNCTION__ , ##args) #define IPRINTI(format,args...) printk(KERN_INFO "ihcp: %s: instance: %d: " format "\n",\ __FUNCTION__, instance , ##args) #define EPRINT(format,args...) printk(KERN_ERR "ihcp: %s: " format "\n",\ __FUNCTION__ , ##args) #define EPRINTI(format,args...) printk(KERN_ERR "ihcp: %s: instance: %d: " format "\n",\ __FUNCTION__, instance , ##args) #define WPRINT(format,args...) printk(KERN_WARNING "ihcp: %s: " format "\n",\ __FUNCTION__ , ##args) #define WPRINTI(format,args...) printk(KERN_WARNING "ihcp: %s: instance: %d: " format "\n",\ __FUNCTION__, instance , ##args) /* * if user dma mode is permitted by install script (AUTO) and kernel * is not configured for more than 4G RAM, use direct user buffer dma. * otherwise use a copy buffer */ #if defined(CONFIG_HIGHMEM64G) || defined(IHCP_DMA_MODE_COPY) #define IHCP_COPY_BUF 1 #define IHCP_DMA_MODE_MSG "user buffer DMA disabled, copy buffer in use\n" #else #define IHCP_COPY_BUF 0 #define IHCP_DMA_MODE_MSG "user buffer DMA enabled\n" #endif /* * use dma_mask if required -- this is probably redundant, since we * are 32 bit DMA capable, and that is supposed to be the default */ #ifdef IHCP_USE_DMA_MASK #define IHCP_PCI_SET_DMA_MASK(pci_dev_p,dma_mask) pci_set_dma_mask(pci_dev_p,dma_mask) #else #define IHCP_PCI_SET_DMA_MASK(pci_dev_p,dma_mask) 0 #endif /* * deal with both old and new request_irq flags */ #ifndef IRQF_SHARED #define IRQF_SHARED SA_SHIRQ #endif /* * old and new ioctl entry points */ #ifdef HAVE_UNLOCKED_IOCTL #define IHCP_UNLOCKED_IOCTL unlocked_ioctl:ihcp_new_ioctl, #else #define IHCP_UNLOCKED_IOCTL #endif #ifdef HAVE_COMPAT_IOCTL #define IHCP_COMPAT_IOCTL compat_ioctl:ihcp_new_ioctl, #else #define IHCP_COMPAT_IOCTL #endif /* * another ugly version test * pt_regs arg to irq_handler being dropped in 2.6.19 * try to stay source compaible across kernels * IHCP__PT_REGS should be defined as in 2.6.19 and later */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) #define IHCP__PT_REGS #else #define IHCP__PT_REGS , struct pt_regs *regs #endif /* * and yet _another_ ugly version test to determine how to access * the statter/gather list */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) #define IHCP_SET_PAGE(sg_p, page_p, len, off) \ sg_set_page(sg_p, page_p, len, off) #else #define IHCP_SET_PAGE(sg_p, page_p, len, off) \ do {\ sg_p->page = page_p;\ sg_p->length = len;\ sg_p->offset = off;\ } while(0) #endif /* * macro definitions for memory mapped board register access * and io control block memory access * * these are gathered here to try to allow for easy portability to * arch's other than i386, where access to Pci board registers, and * the board's access to DMA chain list memory may be byte swizzled * * the PLXx macros are used to access plx registers directly * the IPLXx macros are used to access the plx dma registers via ihcp space - * which is necessary with the early revs of the plx chip - we could use IHCPx macros * to do this, but IPLX should be more readable, and it will be easier to find them * to mod later if necessary for some future rev of the chip * * the configuration accesses will be done explicitly, without macros * * unit_p->xxx_base is defined as a pointer to a 32 bit value, offsets are byte offsets, so /4 * we could also do things with fancy casts * * offset to IOPB_XXXX will be iopb # * IOPB_SIZE + byte offset within the iopb so also /4 * * the previous driver version used direct pointer de-referencing (OK on x86) */ #define PLX_GETL(offset) readl(unit_p->plx_base_p + offset) #define PLX_PUTL(offset,value) writel(value, unit_p->plx_base_p + offset) #define IPLX_GETL(offset) readl(unit_p->ihcp_base_p + offset) #define IPLX_PUTL(offset,value) writel(value, unit_p->ihcp_base_p + offset) #define IHCP_GETL(offset) readl(unit_p->ihcp_base_p + offset) #define IHCP_PUTL(offset,value) writel(value, unit_p->ihcp_base_p + offset) #define IOPB_GETL(offset) readl(unit_p->iopb_base_p + offset) #define IOPB_PUTL(offset,value) writel(value, unit_p->iopb_base_p + offset) #endif /* IHCP_VAR_H */ ./ikonex.c0000644000076400007640000007537310630563126011071 0ustar wdwwdw/* * ikonex.c * hardcopy driver/board test and exercise program * based on IKONdma.c v2.7 by Interface Consultants * * Tahoma Technology (formerly Ikon Corporation) driver * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * * 1 November, 2000 * * 10 February, 2001 define _IOC_SIZEMASK & _IOC_SIZESHIFT for Solaris * * 19 November, 2001 corrected errors in self documenting printfs * made "Tahoma" changes in comments * * 6 May, 2002 changed include file from ihcp_io.h to ikonex.h * added crlf and ff (ascii) commands * updated company info * * 14 June, 2002 changed back to including ihcp_io.h * * 8 July, 2002 corrected mode self doc printf * * 20 April, 2004 hack to try to make interactive * * 8 March, 2007 add endian ioctl support * move IkonRegStruct here and eliminate .h file */ #define _IOC_SIZEMASK 0xFF #define _IOC_SIZESHIFT 16 static char VersionString[] = { "ikonex - 8 March, 2007" }; static char IkonCorp[] = { "Tahoma Technology (formerly Ikon Corporation), Seattle, WA, USA" }; /* * this program outputs files in print or plot DMA mode * and allows issuing assorted commands to the board, * driver, and plotter */ #include #include #include #include #include #include #include #include #include "./ihcp_io.h" #define TRUE 1 #define FALSE 0 struct IkonRegStruct { uint DevandVendID; uint RevID; uint PlxIntControlandStatus; uint PlxEepromControl; uint PlxDmaMode; uint PlxDmaPciAddress; uint PlxDmaLocalAddress; uint PlxDmaTransferCount; uint PlxDmaDescriptorPointer; uint PlxDmaCommandStatusReg; uint InterruptMask; uint Mode; uint DeviceControl; uint InterfaceControl; uint InterfaceStatus; uint DeviceStatus; uint ReverseData; uint AutoLtrCountLow; uint AutoLtrCountHigh; } IkonReg; static int interactive; static int verbose; static char *plot_data; static ulong pd_cnt; static uint timeout; static uint cmd_arg; static int arg_set = 0; static int file_ptr = -1; /*************************************************************************/ void try_exit(void) { if(!interactive) exit(-1); } /*************************************************************************/ int dma_one_block(char *string, ulong byte_count) { ulong rc; if (file_ptr == -1) { printf("Error device not open - use -w<> at start of command\n"); return 1; } rc = write(file_ptr, string, byte_count); if (rc != byte_count) { perror("Error on write in dma_one_block"); return 1; } return 0; } /*************************************************************************/ int dma_file(char *file_name) { int file_handle; uint rc; char *buff1; char *buff2; char *buff; ulong buff_cnt; ulong rd_cnt; if (file_ptr == -1) { printf("Error device not open - use -w<> at start of command\n"); return 1; } /* open the file */ file_handle = open(file_name, O_RDONLY); if (file_handle == -1) { printf("Error - unable to open file: %s\n", file_name); perror(""); return 1; } /* allocate the data buffers */ buff1 = (char *) malloc(pd_cnt); if (buff1 == (char *) NULL) { printf("Error - unable to allocate 1st buffer: %ld bytes\n", pd_cnt); perror(""); return 1; } if (verbose == TRUE) printf("Allocated buff1 of %ld bytes\n", pd_cnt); buff2 = (char *) malloc(pd_cnt); if (buff2 == (char *) NULL) { printf("Error - unable to allocate 2nd buffer: %ld bytes\n", pd_cnt); perror(""); return 1; } if (verbose == TRUE) printf("Allocated buff2 of %ld bytes\n", pd_cnt); /* set the first buffer pointer */ plot_data = buff1; /* output the file */ /* * ikon note: Ikon's drivers do not return from write * until the dma buffer has been completely transferred * to the board's fifos. Double buffering is not required. */ while (1) { /* read a buffer full */ buff_cnt = 0; while (1) { rd_cnt = pd_cnt - buff_cnt; /* how many left to read */ if (rd_cnt > 65534L) { /* limit each read to max */ rd_cnt = 65534L; } buff = (char *) ((char *) plot_data + buff_cnt); rc = read(file_handle, buff, (uint) rd_cnt); /* can only read 64K */ if (rc == -1) { perror("Error on read file in dma_file"); return 1; } buff_cnt += rc; if (buff_cnt >= pd_cnt) { /*printf("Buffer Full\n"); */ break; } if (rc == 0) { /*printf("End of File rc==0\n"); */ break; } } /* output the buffer if there is anything in it */ if (buff_cnt != 0) { /*printf("Outputting one buffer cnt=%lu\n",buff_cnt); */ if(dma_one_block(plot_data, buff_cnt)) { printf("dma_one_block error!\n"); return 1; } } else { /*printf("Not Outputting one buffer cnt=%lu\n",buff_cnt); */ break; } /* toggle the buffer pointers */ if (plot_data == buff1) { plot_data = buff2; } else { plot_data = buff1; } } if (buff1 != (char *) NULL) { free(buff1); if (verbose == TRUE) printf("Freed buff1\n"); } if (buff2 != (char *) NULL) { free(buff2); if (verbose == TRUE) printf("Freed buff2\n"); } return 0; } /*************************************************************************/ int issue_cmd(char *cmd_str) { int rc; uint local_arg; if (verbose == TRUE) printf("issue_cmd cmd_str = %s\n", cmd_str); if (file_ptr == -1) { printf("Error device not open - use -w<> at start of command\n"); return 1; } /* SPEED COMMANDS */ if (!strcmp(cmd_str, "SPEED_0")) { if (verbose == TRUE) printf("cmd = SPEED_0\n"); local_arg = IHCPIO_SPEED0; rc = ioctl(file_ptr, IHCPIO_SET_CONFIG, &local_arg); if (rc != 0) { perror("Error on ioctl SPEED_0\n"); return 1; } return 0; } if (!strcmp(cmd_str, "SPEED_1")) { if (verbose == TRUE) printf("cmd = SPEED_1\n"); local_arg = IHCPIO_SPEED1; rc = ioctl(file_ptr, IHCPIO_SET_CONFIG, &local_arg); if (rc != 0) { perror("Error on ioctl SPEED_1\n"); return 1; } return 0; } if (!strcmp(cmd_str, "SPEED_2")) { if (verbose == TRUE) printf("cmd = SPEED_2\n"); local_arg = IHCPIO_SPEED2; rc = ioctl(file_ptr, IHCPIO_SET_CONFIG, &local_arg); if (rc != 0) { perror("Error on ioctl SPEED_2\n"); return 1; } return 0; } if (!strcmp(cmd_str, "SPEED_3")) { if (verbose == TRUE) printf("cmd = SPEED_3\n"); local_arg = IHCPIO_SPEED3; rc = ioctl(file_ptr, IHCPIO_SET_CONFIG, &local_arg); if (rc != 0) { perror("Error on ioctl SPEED_3\n"); return 1; } return 0; } /* MODE COMMANDS */ if (!strcmp(cmd_str, "VPRINT")) { if (verbose == TRUE) printf("cmd = VPRINT\n"); local_arg = IHCPIO_V_NPRINT;; rc = ioctl(file_ptr, IHCPIO_SET_VMODE, &local_arg); if (rc != 0) { perror("Error on ioctl VPRINT\n"); return 1; } return 0; } if (!strcmp(cmd_str, "VPLOT")) { if (verbose == TRUE) printf("cmd = VPLOT\n"); local_arg = IHCPIO_V_PLOT; rc = ioctl(file_ptr, IHCPIO_SET_VMODE, &local_arg); if (rc != 0) { perror("Error on ioctl VPLOT\n"); return 1; } return 0; } if (!strcmp(cmd_str, "VPRINTPLOT")) { if (verbose == TRUE) printf("cmd = VPRINTPLOT\n"); local_arg = IHCPIO_V_SPP; rc = ioctl(file_ptr, IHCPIO_SET_VMODE, &local_arg); if (rc != 0) { perror("Error on ioctl VPRINTPLOT\n"); return 1; } return 0; } if (!strcmp(cmd_str, "DEV_RESET")) { if (verbose == TRUE) printf("cmd = DEV_RESET\n"); rc = ioctl(file_ptr, IHCPIO_DEV_RESET, &local_arg); if (rc != 0) { perror("Error on ioctl DEV_RESET\n"); return 1; } return 0; } /* PULSED COMMANDS */ if (!strcmp(cmd_str, "VCLR")) { if (verbose == TRUE) printf("cmd = VCLR\n"); local_arg = IHCPIO_VCLR; rc = ioctl(file_ptr, IHCPIO_V_CMD, &local_arg); if (rc != 0) { perror("Error on ioctl VCLR\n"); return 1; } return 0; } if (!strcmp(cmd_str, "VFED")) { if (verbose == TRUE) printf("cmd = VFED\n"); local_arg = IHCPIO_VFED; rc = ioctl(file_ptr, IHCPIO_V_CMD, &local_arg); if (rc != 0) { perror("Error on ioctl VFED\n"); return 1; } return 0; } if (!strcmp(cmd_str, "VEOT")) { if (verbose == TRUE) printf("cmd = VEOT\n"); local_arg = IHCPIO_VEOT; rc = ioctl(file_ptr, IHCPIO_V_CMD, &local_arg); if (rc != 0) { perror("Error on ioctl VEOT\n"); return 1; } return 0; } if (!strcmp(cmd_str, "VLTR")) { if (verbose == TRUE) printf("cmd = VLTR\n"); local_arg = IHCPIO_VLTR; rc = ioctl(file_ptr, IHCPIO_V_CMD, &local_arg); if (rc != 0) { perror("Error on ioctl VLTR\n"); return 1; } return 0; } /* GET REGISTERS */ if (!strcmp(cmd_str, "GET_REGS")) { if (verbose == TRUE) printf("cmd = GET_REGS\n"); rc = ioctl(file_ptr, IHCPIO_GET_REGS, &IkonReg); if (rc != 0) { perror("Error on ioctl , GET_REGS\n"); return 1; } printf("\n"); printf("GET_REGS - Device & Vendor ID = 0x%x\n", IkonReg.DevandVendID); printf("GET_REGS - Revision ID = 0x%x\n", IkonReg.RevID); printf("GET_REGS - PLX Interrupt Control/Status = 0x%x\n", IkonReg.PlxIntControlandStatus); printf("GET_REGS - PLX EEPROM Control = 0x%x\n", IkonReg.PlxEepromControl); printf("GET_REGS - PLX DMA Mode = 0x%x\n", IkonReg.PlxDmaMode); printf("GET_REGS - PLX DMA Pci Address = 0x%x\n", IkonReg.PlxDmaPciAddress); printf("GET_REGS - PLX DMA Local Address = 0x%x\n", IkonReg.PlxDmaLocalAddress); printf("GET_REGS - PLX DMA Transfer Count = 0x%x\n", IkonReg.PlxDmaTransferCount); printf("GET_REGS - PLX DMA Descriptor Pointer = 0x%x\n", IkonReg.PlxDmaDescriptorPointer); printf("GET_REGS - PLX DMA Command/Status Reg. = 0x%x\n", IkonReg.PlxDmaCommandStatusReg); printf("GET_REGS - Interrupt Mask = 0x%x\n", IkonReg.InterruptMask & 0xFF); printf("GET_REGS - Mode = 0x%x\n", IkonReg.Mode & 0xFF); printf("GET_REGS - Device Control = 0x%x\n", IkonReg.DeviceControl & 0xFF); printf("GET_REGS - Interface Control = 0x%x\n", IkonReg.InterfaceControl & 0xFF); printf("GET_REGS - Interface Status = 0x%x\n", IkonReg.InterfaceStatus & 0xFF); printf("GET_REGS - Device Status = 0x%x\n", IkonReg.DeviceStatus & 0xFF); printf("GET_REGS - Reverse Data = 0x%x\n", IkonReg.ReverseData & 0xFF); printf("GET_REGS - Auto Ltr Count Low = 0x%x\n", IkonReg.AutoLtrCountLow & 0xFF); printf("GET_REGS - Auto Ltr Count High = 0x%x\n", IkonReg.AutoLtrCountHigh & 0xff); return 0; } /* GET STATUS */ if (!strcmp(cmd_str, "GET_STATUS")) { if (verbose == TRUE) printf("cmd = GET_STATUS\n"); rc = ioctl(file_ptr, IHCPIO_GET_STATUS, &local_arg); if (rc != 0) { perror("Error on ioctl GET_STATUS\n"); return 1; } printf("GET_STATUS - status = 0x%x\n", local_arg); if (local_arg & IHCPIO_DEV_RDY) printf("GET_STATUS - Device Ready\n"); if (local_arg & IHCPIO_DEV_BUSY) printf("GET_STATUS - Device Busy\n"); if (local_arg & IHCPIO_DEV_FAULT) printf("GET_STATUS - Device Fault\n"); if (local_arg & IHCPIO_DEV_POUT) printf("GET_STATUS - Device Paper Out\n"); if (local_arg & IHCPIO_DEV_SEL) printf("GET_STATUS - Device Online\n"); return 0; } /* GET BOARD */ if (!strcmp(cmd_str, "GET_BOARD")) { if (verbose == TRUE) printf("cmd = GET_BOARD\n"); rc = ioctl(file_ptr, IHCPIO_GET_BOARD, &local_arg); if (rc != 0) { perror("Error on ioctl GET_BOARD\n"); return 1; } printf("GET_BOARD - Board = 0x%x\n", local_arg); if (local_arg == IHCPIO_VERS_DIFF) printf("GET_BOARD - Strapping is Versatec Differential\n"); if (local_arg == IHCPIO_VERS_TTL) printf("GET_BOARD - Strapping is Versatec TTL\n"); if (local_arg == IHCPIO_CENT) printf("GET_BOARD - Strapping is Centronics\n"); return 0; } /* GET FLAGS */ if (!strcmp(cmd_str, "GET_FLAGS")) { if (verbose == TRUE) printf("cmd = GET_FLAGS\n"); rc = ioctl(file_ptr, IHCPIO_GET_FLAGS, &local_arg); if (rc != 0) { perror("Error on ioctl GET_FLAGS\n"); return 1; } printf("GET_FLAGS - Flags = 0x%x\n", local_arg); return 0; } /* GET FIFO */ if (!strcmp(cmd_str, "GET_FIFO")) { if (verbose == TRUE) printf("cmd = GET_FIFO\n"); rc = ioctl(file_ptr, IHCPIO_GET_FIFO, &local_arg); if (rc != 0) { perror("Error on ioctl GET_FIFO\n"); return 1; } printf("GET_FIFO - FIFO = 0x%x\n", local_arg); if (local_arg & IHCPIO_FIFO_FULL) printf("GET_FIFO - FIFO Full\n"); if (local_arg & IHCPIO_FIFO_HALF) printf("GET_FIFO - FIFO Half Full\n"); if (local_arg & IHCPIO_FIFO_EMPTY) printf("GET_FIFO - FIFO Empty\n"); return 0; } /* PCI_ID */ if (!strcmp(cmd_str, "PCI_ID")) { if (verbose == TRUE) printf("cmd = PCI_ID\n"); rc = ioctl(file_ptr, IHCPIO_DEV_AND_VEND_ID, &local_arg); if (rc != 0) { perror("Error on ioctl DEV_AND_VEND_ID\n"); return 1; } printf("PCI_ID - Device and Vendor IDs = 0x%x\n", local_arg); return 0; } /* INTERFACE STATUS */ if (!strcmp(cmd_str, "INT_STATUS")) { if (verbose == TRUE) printf("cmd = INT_STATUS\n"); rc = ioctl(file_ptr, IHCPIO_INTERFACE_STATUS, &local_arg); if (rc != 0) { perror("Error on ioctl INTERFACE_STATUS\n"); return 1; } printf("INT_STATUS - Status = 0x%x\n", local_arg); return 0; } /* DEVICE STATUS */ if (!strcmp(cmd_str, "DEV_STATUS")) { if (verbose == TRUE) printf("cmd = DEV_STATUS\n"); rc = ioctl(file_ptr, IHCPIO_DEVICE_STATUS, &local_arg); if (rc != 0) { perror("Error on ioctl DEVICE_STATUS\n"); return 1; } printf("DEV_STATUS - Status = 0x%x\n", local_arg); return 0; } /* REVERSE DATA */ if (!strcmp(cmd_str, "REV_DATA")) { if (verbose == TRUE) printf("cmd = REV_DATA\n"); rc = ioctl(file_ptr, IHCPIO_REVERSE_DATA, &local_arg); if (rc != 0) { perror("Error on ioctl REVERSE_DATA\n"); return 1; } printf("REV_DATA - Reverse Data = 0x%x\n", local_arg); return 0; } /* READY WAIT */ if (!strcmp(cmd_str, "RDY_WAIT")) { if (verbose == TRUE) printf("cmd = RDY_WAIT\n"); rc = ioctl(file_ptr, IHCPIO_RDY_WAIT, &local_arg); if (rc != 0) { perror("Error on ioctl RDY_WAIT\n"); return 1; } printf("RDY_WAIT - wait complete\n"); return 0; } /* HALF WAIT */ if (!strcmp(cmd_str, "HALF_WAIT")) { if (verbose == TRUE) printf("cmd = HALF_WAIT\n"); rc = ioctl(file_ptr, IHCPIO_HALF_WAIT, &local_arg); if (rc != 0) { perror("Error on ioctl HALF_WAIT\n"); return 1; } printf("HALF_WAIT - wait complete\n"); return 0; } /* STREAM ON */ if (!strcmp(cmd_str, "STREAM_ON")) { if (verbose == TRUE) printf("cmd = STREAM_ON\n"); rc = ioctl(file_ptr, IHCPIO_STREAM_ON, &local_arg); if (rc != 0) { perror("Error on ioctl STREAM_ON\n"); return 1; } return 0; } /* STREAM OFF */ if (!strcmp(cmd_str, "STREAM_OFF")) { if (verbose == TRUE) printf("cmd = STREAM_OFF\n"); rc = ioctl(file_ptr, IHCPIO_STREAM_OFF, &local_arg); if (rc != 0) { perror("Error on ioctl STREAM_OFF\n"); return 1; } return 0; } /* MASTER CLEAR */ if (!strcmp(cmd_str, "MASTER_CLEAR")) { if (verbose == TRUE) printf("cmd = MASTER_CLEAR\n"); rc = ioctl(file_ptr, IHCPIO_MASTER_CLEAR, &local_arg); if (rc != 0) { perror("Error on ioctl MASTER_CLEAR\n"); return 1; } return 0; } /* SOFT ACK */ if (!strcmp(cmd_str, "SOFT_ACK")) { if (verbose == TRUE) printf("cmd = SOFT_ACK\n"); rc = ioctl(file_ptr, IHCPIO_SOFT_ACK, &local_arg); if (rc != 0) { perror("Error on ioctl SOFT_ACK\n"); return 1; } return 0; } /* AUTO LTR ON */ if (!strcmp(cmd_str, "AUTO_LTR_ON")) { if (verbose == TRUE) printf("cmd = AUTO_LTR_ON\n"); rc = ioctl(file_ptr, IHCPIO_AUTO_LTR_ON, &local_arg); if (rc != 0) { perror("Error on ioctl AUTO_LTR_ON\n"); return 1; } return 0; } /* AUTO LTR OFF */ if (!strcmp(cmd_str, "AUTO_LTR_OFF")) { if (verbose == TRUE) printf("cmd = AUTO_LTR_OFF\n"); rc = ioctl(file_ptr, IHCPIO_AUTO_LTR_OFF, &local_arg); if (rc != 0) { perror("Error on ioctl AUTO_LTR_OFF\n"); return 1; } return 0; } /* BIG ENDIAN */ if (!strcmp(cmd_str, "BIG_ENDIAN")) { if (verbose == TRUE) printf("cmd = BIG_ENDIAN\n"); rc = ioctl(file_ptr, IHCPIO_BIG_ENDIAN, &local_arg); if (rc != 0) { perror("Error on ioctl BIG_ENDIAN\n"); return 1; } return 0; } /* LITTLE ENDIAN */ if (!strcmp(cmd_str, "LITTLE_ENDIAN")) { if (verbose == TRUE) printf("cmd = LITTLE_ENDIAN\n"); rc = ioctl(file_ptr, IHCPIO_LITTLE_ENDIAN, &local_arg); if (rc != 0) { perror("Error on ioctl LITTLE_ENDIAN\n"); return 1; } return 0; } /* SET CONFIG */ if (!strcmp(cmd_str, "SET_CONFIG")) { if (verbose == TRUE) printf("cmd = SET_CONFIG\n"); if (!arg_set) { printf("Error - SET_CONFIG requires argument\n"); exit(-1); } rc = ioctl(file_ptr, IHCPIO_SET_CONFIG, &cmd_arg); if (rc != 0) { perror("Error on ioctl SET_CONFIG\n"); return 1; } return 0; } /* AUTO LTR COUNT */ if (!strcmp(cmd_str, "AUTO_LTR_COUNT")) { if (verbose == TRUE) printf("cmd = AUTO_LTR_COUNT\n"); if (!arg_set) { printf("Error - AUTO_LTR_COUNT requires argument\n"); return 1; } rc = ioctl(file_ptr, IHCPIO_AUTO_LTR_COUNT, &cmd_arg); if (rc != 0) { perror("Error on ioctl AUTO_LTR_COUNT\n"); return 1; } return 0; } /* DIRECT MODE */ if (!strcmp(cmd_str, "DIRECT_MODE")) { if (verbose == TRUE) printf("cmd = DIRECT_MODE\n"); if (!arg_set) { printf("Error - DIRECT_MODE requires argument\n"); return 1; } rc = ioctl(file_ptr, IHCPIO_DIRECT_MODE, &cmd_arg); if (rc != 0) { perror("Error on ioctl DIRECT_MODE\n"); return 1; } return 0; } /* DEVICE CONTROL */ if (!strcmp(cmd_str, "DEV_CONTROL")) { if (verbose == TRUE) printf("cmd = DEV_CONTROL\n"); if (!arg_set) { printf("Error - DEV_CONTROL requires argument\n"); return 1; } rc = ioctl(file_ptr, IHCPIO_DEVICE_CONTROL, &cmd_arg); if (rc != 0) { perror("Error on ioctl DEVICE_CONTROL\n"); return 1; } return 0; } printf("Error - invalid command: %s\n", cmd_str); return 1; } /*************************************************************************/ void useage(void) { char dummystr[8]; printf("\nUsage: ikonex -w<> [-d<>] [-m<>] [-f<>] [-b<>] [-a<>] [-c<>] [-s<>] [-n] [-p] [-v] [-t<>] [-i] [-h] [-q]\n"); printf(" -w (required- e.g. /dev/ihcp0)\n"); printf(" -d (optional - default=16 max=128)\n"); printf(" -m mode for transfer, PRINT or PLOT\n"); printf(" -f data file to dma to port\n"); printf(" -b output single hex byte using p-i/o\n"); printf(" -a hex argument required (*) for some commands\n"); printf(" -c remote command to board/plotter, can be:\n"); printf(" \n"); printf(" SPEED_0 - slowest handshake speed\n"); printf(" SPEED_1\n"); printf(" SPEED_2\n"); printf(" SPEED_3 - fastest handshake speed\n"); printf(" VPRINT - set Versatec print mode\n"); printf(" VPLOT - set Versatec plot mode\n"); printf("\n--More-- (enter)\n"); fgets(dummystr, 2, stdin); printf(" VPRINTPLOT - set Versatec simultaneous print/plot mode\n"); printf(" DEV_RESET - send reset pulse to plotter\n"); printf(" VCLR - Versatec Remote Clear\n"); printf(" VFED - Versatec Remote Form Feed\n"); printf(" VEOT - Versatec End or Terminate\n"); printf(" VLTR - Versatec Remote Line Terminate\n"); printf(" GET_REGS - print board registers\n"); printf(" GET_STATUS - print formatted device status\n"); printf(" GET_BOARD - print board interface strapping\n"); printf(" GET_FLAGS - print driver internal flags\n"); printf(" GET_FIFO - print fifo status\n"); printf(" PCI_ID - print device and vendor IDs\n"); printf(" INT_STATUS - print interface status register\n"); printf("\n--More-- (enter)\n"); fgets(dummystr, 2, stdin); printf(" DEV_STATUS - print device status\n"); printf(" REV_DATA - print contents of reverse data register\n"); printf(" RDY_WAIT - wait for board and plotter ready\n"); printf(" HALF_WAIT - wait for fifo <1/2 full\n"); printf(" STREAM_ON - set Centronics data streaming mode\n"); printf(" STREAM_OFF - clear data streaming mode\n"); printf(" MASTER_CLEAR - the big hammer (use wisely)\n"); printf(" SOFT_ACK - issue software ack to board\n"); printf(" AUTO_LTR_ON enable auto line terminate (Versatec only)\n"); printf(" AUTO_LTR_OFF disable auto line terminate\n"); printf(" BIG_ENDIAN set byte ordering to big endian\n"); printf(" LITTLE_ENDIAN set byte ordering to little endian\n"); printf(" \n"); printf("\n--More-- (enter)\n"); fgets(dummystr, 2, stdin); printf(" * SET_CONFIG - set handshake speed and mode to \n"); printf(" * AUTO_LTR_COUNT - set auto line terminate count to \n"); printf(" * DIRECT_MODE - write to mode register\n"); printf(" * DEV_CONTROL - write to device control register\n"); printf(" \n"); printf(" -s output string to plotter (in current print/plot mode)\n"); printf(" **** strings must be in double quotes ****\n"); printf(" -n output carriage return/line feed pair of ascii characters (in current mode\n"); printf(" -p output carriage return/form feed pair of ascii characters (in current mode)\n"); printf(" \n"); printf(" -t set dma and ready wait timeouts in seconds\n"); printf(" -v toggle verbose mode\n"); printf(" -i enter interactive mode. once in interactive mode all above options may\n"); printf(" be entered **one per line** w/or w/out the leading -\n"); printf(" -h print this help info\n"); printf(" -q exit interactive mode\n"); printf("\n--More-- (enter)\n"); fgets(dummystr, 2, stdin); printf(" \n"); printf("Note: args are executed left to right as found, multiple files in \n"); printf(" different modes can be sent in a single command.\n"); printf(" e.g. ikonex -w/dev/ihcp0 -mPRINT -ffile1.prn -mPLOT -ffile2.plt\n"); printf(" sends file1.prn in print mode, and then file2.plt in plot mode\n"); printf(" commands requiring arguments MUST be preceded by -a\n"); printf(" \n"); printf(" In interactive mode, enter one cmd/value or a per line\n"); printf(" \n"); } /*************************************************************************/ int main(int argc, char **argv) { char *mode_str = NULL; char *file_str = NULL; char *cmd_str = NULL; char *dma_str = NULL; char *timeout_str = NULL; char *arg_str = NULL; uint ioctl_cmd; uint dma_size; uint local_arg; char linestr[132]; char *lineptr; int c; char cstr[32]; char *loptarg = NULL; char loptstr[132]; int rc; /*************************************************************/ /* print opening banner */ /*************************************************************/ printf("\nTahoma Technology driver exerciser and utility output program - %s\n", VersionString); printf(" %s\n\n", IkonCorp); if (argc == 1) { useage(); exit(-1); } interactive = 0; arg_set = 0; verbose = FALSE; pd_cnt = 16 * 1024; /* 16kb default */ /*****************************************************************/ /* get command line or interactive arguments */ /*****************************************************************/ while (1) { if(interactive) { /* * scan for the option flag and argument * avoid any leading '-' and if s (string) * scan for characters between double quotes */ printf("enter command (h = help, q = quit): "); strcpy(linestr,"z"); strcpy(cstr,"z"); strcpy(loptstr,"z"); fgets(linestr, 130, stdin); if((lineptr = strchr(linestr, '-')) != NULL) lineptr++; else lineptr = linestr; sscanf(lineptr, "%1s %130s", cstr, loptstr); c = cstr[0]; if(c == 's') sscanf(lineptr, "%1s \"%[^\"]", &c, loptstr); loptarg = loptstr; } else { c = getopt(argc, argv, "ihqw:d:m:f:b:a:c:s:t:vnp"); loptarg = optarg; } if (c != EOF) { c = tolower(c); switch (c) { case 'q': printf("goodbye...\n"); exit(0); break; case 'i': if (verbose == TRUE) printf("interactive mode set\n"); interactive = TRUE; break; case 'h': useage(); break; case 'w': file_str = loptarg; if(file_ptr > 0) { if(verbose) printf("closing previously open device\n"); close(file_ptr); } if (verbose == TRUE) printf("opening device = %s\n", loptarg); file_ptr = open(loptarg, O_WRONLY); if (file_ptr == -1) { printf("Error opening device %s: \n", loptarg); perror(""); try_exit(); } break; case 'c': cmd_str = loptarg; if (verbose == TRUE) printf("cmd_str = %s\n", cmd_str); if (issue_cmd(cmd_str)) try_exit(); arg_set = 0; break; case 'd': dma_str = loptarg; if (verbose == TRUE) printf("dma_str = %s\n", dma_str); dma_size = (uint) strtol(dma_str, (char **) NULL, 0); if (verbose == TRUE) printf("dma_size = %d\n", dma_size); if (dma_size > 128) { printf("Warning - DMA size of %d exceeds max using DMA size = 128\n", dma_size); dma_size = 128; } pd_cnt = (ulong) dma_size *1024L; break; case 'f': file_str = loptarg; if (verbose == TRUE) printf("file_str = %s\n", file_str); if(dma_file(file_str)) printf("dma_file error!\n"); break; case 'b': arg_str = loptarg; if (verbose == TRUE) printf("byte_str = %s\n", arg_str); local_arg = (uint) strtol(arg_str, (char **) NULL, 0); if (verbose == TRUE) printf("data byte = 0x%x\n", local_arg); if (file_ptr == -1) { printf("Error device not open - use -w<> at start of command\n"); try_exit(); } rc = ioctl(file_ptr, (IHCPIO_DATA_OUT | 1 << _IOC_SIZESHIFT), &local_arg); if (rc != 0) { perror("Error on ioctl DATA_OUT\n"); try_exit(); } break; case 'm': mode_str = loptarg; if (verbose == TRUE) printf("mode_str = %s\n", mode_str); if (file_ptr == -1) { printf("Error device not open - use -w<> at start of command\n"); try_exit(); } if (!strcmp(mode_str, "PLOT")) { if (verbose == TRUE) printf("mode = PLOT\n"); local_arg = IHCPIO_V_PLOT; rc = ioctl(file_ptr, IHCPIO_SET_VMODE, &local_arg); if (rc != 0) { perror("Error on ioctl SET_VMODE\n"); try_exit(); } } else if (!strcmp(mode_str, "PRINT")) { if (verbose == TRUE) printf("mode = PRINT\n"); local_arg = IHCPIO_V_NPRINT; rc = ioctl(file_ptr, IHCPIO_SET_VMODE, &local_arg); if (rc != 0) { perror("Error on ioctl SET_VMODE\n"); try_exit(); } } else { printf("Error: mode not PRINT or PLOT: %s \n", mode_str); try_exit(); } break; case 'v': if (verbose == TRUE) { printf("verbose = FALSE\n"); verbose = FALSE; } else { printf("verbose = TRUE\n"); verbose = TRUE; } break; case 't': timeout_str = loptarg; if (verbose == TRUE) printf("timeout_str = %s\n", timeout_str); if (file_ptr == -1) { printf("Error device not open - use -w<> at start of command\n"); try_exit(); } timeout = (uint) strtol(timeout_str, (char **) NULL, 0); if (verbose == TRUE) printf("timeout = %d\n", timeout); rc = ioctl(file_ptr, IHCPIO_SET_DMATIME, &timeout); if (rc != 0) { perror("Error on ioctl SET_DMATIME\n"); try_exit(); } rc = ioctl(file_ptr, IHCPIO_SET_FIFOTIME, &timeout); if (rc != 0) { perror("Error on ioctl SET_FIFOTIME\n"); try_exit(); } break; case 'a': arg_str = loptarg; if (verbose == TRUE) printf("arg_str = %s\n", arg_str); cmd_arg = (uint) strtol(arg_str, (char **) NULL, 0); if (verbose == TRUE) printf("cmd_arg = 0x%x\n", cmd_arg); arg_set = 1; break; case 's': arg_str = loptarg; if (verbose == TRUE) printf("arg_str = %s\n", arg_str); if (file_ptr == -1) { printf("Error device not open - use -w<> at start of command\n"); try_exit(); } ioctl_cmd = IHCPIO_DATA_OUT | ((strlen(arg_str) & _IOC_SIZEMASK) << _IOC_SIZESHIFT); rc = ioctl(file_ptr, ioctl_cmd, arg_str); if (rc != 0) { perror("Error on ioctl DATA_OUT\n"); try_exit(); } break; case 'n': if (verbose == TRUE) printf("sending CR/LF\n"); if (file_ptr == -1) { printf("Error device not open - use -w<> at start of command\n"); try_exit(); } ioctl_cmd = IHCPIO_DATA_OUT | ((2 & _IOC_SIZEMASK) << _IOC_SIZESHIFT); rc = ioctl(file_ptr, ioctl_cmd, "\r\n"); if (rc != 0) { perror("Error on ioctl DATA_OUT\n"); try_exit(); } break; case 'p': if (verbose == TRUE) printf("sending CR/FF\n"); if (file_ptr == -1) { printf("Error device not open - use -w<> at start of command\n"); try_exit(); } ioctl_cmd = IHCPIO_DATA_OUT | ((2 & _IOC_SIZEMASK) << _IOC_SIZESHIFT); rc = ioctl(file_ptr, ioctl_cmd, "\r\f"); if (rc != 0) { perror("Error on ioctl DATA_OUT\n"); try_exit(); } break; default: printf("Invalid option \"%c\"\n", c); break; } } else { if (verbose == TRUE) printf("End of arg list\n"); break; } } return (0); } /*************************************************************************/ ./iktest0.c0000644000076400007640000000467010526160331011142 0ustar wdwwdw/* * iktest0.c * * simple(!) sample program that opens ihpc0, * calls ioctl to set the dma timeout to 10 seconds, * sets to print mode if VPI interface selected * calls write to output a repeating message, * calls write again to do a form feed, and then dumps the registers * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * ????? initial coding * * 6 May, 2002 updated company info and added VPI print mode selection * and banner * 17 May, 2002 corrected typo in printf string * * 17 June, 2002 corrected version string * * 5 October, 2005 added includes to keep newer versions of Linux happy, * converted ioctl arg to __u32 to preserve size in 64 bit mode */ #include #include #include #include #include #include "./ihcp_io.h" /* also picks up types.h for __u32 */ #define DEVICE "/dev/ihcp0" static char version_string[] = { "iktest0, 31 October, 2005"}; static char tahoma_banner[] ={ "Tahoma Technology (formerly Ikon Corporation), Seattle, WA, USA"}; main() { int i, err, filedes; __u32 argarray[19]; printf("\n%s\n",version_string); printf("%s\n", tahoma_banner); filedes = open(DEVICE, O_RDWR); if(filedes == -1) { perror("error on open"); exit(0); } printf("filedescriptor = 0x%x\n", filedes); argarray[0] = 10; err = ioctl(filedes, IHCPIO_SET_DMATIME, argarray); if(err == -1) { perror("error on SET_DMATIME ioctl"); exit(0); } err = ioctl(filedes, IHCPIO_GET_BOARD, argarray); if(err == -1) { perror("error on GET_BOARD ioctl"); exit(0); } if(argarray[0] == IHCPIO_CENT) printf("board strapped for CENTRONICS\n"); else { printf("board strapped for VPI - setting PRINT mode\n"); argarray[0] = IHCPIO_V_NPRINT; err = ioctl(filedes, IHCPIO_SET_VMODE, argarray); if(err == -1) { perror("error on SET_VMODE ioctl"); exit(0); } } for (i = 0; i < 10; i++) { err = write(filedes, "hello world!\r\n", 14); if (err == -1) { perror("error on write call #1"); exit(0); } } err = write(filedes, "\r\f", 2); if (err == -1) { perror("error on write call #2"); exit(0); } err = ioctl(filedes, IHCPIO_GET_REGS, argarray); if (err == -1) { perror("error on GET_REGS ioctl"); exit(0); } for (i = 0; i < 19; i++) printf("arg[%d] = 0x%x\n", i, argarray[i]); printf("DONE\n"); } ./Makefile0000644000076400007640000000050510526160331011044 0ustar wdwwdw# kbuild style makefile for Tahoma Technology PCI Hardcopy driver # # Tahoma Technology # (formerly Ikon Corporation) # 107 2nd Avenue North # Seattle, WA, USA 98109 # # 206.728.6465 # http://www.tahomatech.com # tahoma@tahomatech.com # # 27 August, 2004 obj-m := ihcp.o ihcp-objs := ihcp_driver.o clean-files := *.o *.ko ./README.ihcp-2.60000644000076400007640000004012010630574551011517 0ustar wdwwdwLinux 2.6.x driver installation instructions and notes for Tahoma Technology's PCI Hardcopy interface Model 10117. Tahoma Technology (formerly IKON Corporation) 107 2nd Avenue, Seattle, WA, USA 98109 206.728.6465 http://www.tahomatech.com tahoma@tahomatech.com About Tahoma Technology: Tahoma Technology has purchased certain of the assets of Ikon Corporation from IKON Office Solutions (IOS). That's the legal version. What has happened is that the people who started and operated Ikon Corporation initially and under IOS have purchased the division back. We continue the same product line and support. References to "Ikon" here and elsewhere are left in place for historical and compatibility reasons. Tahoma/Ikon code and documentation has been previously, and remains, in the public domain. Driver Installation & Notes: This version of the driver was tested under Linux 2.6. It is NOT compatible with earlier versions of Linux. The initial release of this driver was the first that did DMA direct from user buffers, using the kiobuf capability in Linux 2.3/2.6. This version moves the user buffer mapping code into the driver itself - since kiobufs are not available in 2.6x. If the kernel has been configured for greater than 4G of RAM, the driver will be compiled to use a kernel copy buffer instead of mapping user buffers for DMA. Copy buffer mode may also be forced by an install script option. In x86_64 systems, the available iommu is used to map high memory addresses to 32 bit address space, so a copy buffer is not necessary. Hopefully our HIGHMEM config detection won't trip on x86_64 systems. The install script also attempts to autodetect the appropriate include directory: /lib/modules/`uname -r/build/inlcude and /usr/src/linux, in that order. NOTE: the driver and install script now support both 10115 and 10117 boards. This document is written assuming a 10117, but it also applies to the 10115. Board types may be mixed in a system. Other install script options are described below. Other files included in this release: ihcp_driver.c code source module for the Linux PCI hardcopy driver ihcp_var.h structure definition file for the PCI hardcopy driver ihcp_reg.h register definition file for the PCI hardcopy driver ihcp_io.h ioctl command definition file for the PCI hardcopy driver - to be #include'd' in the calling program ihcp_changes.h change dates and descriptions & module ID macros for the driver Makefile simple makefile for the new kbuild system ihcp_install-2.6 used to compile and load driver module iktest0.c very simple sample program that calls the hardcopy driver filtertest.c simple filter that adds carriage returns to text files for printing (on laserjet, for example) ikonex.c command line or interactive driver/board/plotter exercise & test utility The hardcopy driver is a module that can be loaded into a running kernel. Before loading, the driver should be compiled on the target machine, and the object module placed in an appropriate /lib/module directory. The install script will do this automatically. ihcp_install-2.6: ihcp_install-2.6 can be used to compile, load, and enable boot loading of the driver, or may be used to manually perform each of these steps individually. The ihcp_install-2.6 script provided can be used to compile the standard or debug version of the driver and place it in the correct directory. To compile the object and place in /lib/modules/`uname -r`/misc: (as super user) ./ihcp_install-2.6 compile To compile the debug version of the driver: (as super user) ./ihcp_install-2.6 compile debug Note that the debug version of the driver generates numerous printk() calls, and should only be used for diagnostic purposes! The ihcp_install-2.6 script provided will load the module and create the device node(s) in the /dev directory. Node names will be ihcp0, ihcp1, ... To load the driver module into a running kernel: (as super user) ihcp_install-2.6 load Once loaded, the driver will be available until unloaded or the system is rebooted. To cause the driver to be loaded when the system is booted: (as super user) ihcp_install-2.6 autoload This will create /etc/ihcp (if not already present) and copy ihcp_install-2.6 to /etc/ihcp. It also adds a line to the end of /etc/rc.d/rc.local which will invoke "/etc/ihcp/ihcp_install-2.6 load" at boot time. Directory and script permissions are set to rwx access for root, and read only for everyone else. When the install script is invoked via rc.local at boot time, standard and error outputs are logged to /etc/ihcp/ihcp_install-2.6.bootlog. In the event of problems with autoloading the driver at boot time, inspect this log for possible diagnostic information. NOTE that any changes made to the local copy of ihcp_install-2.6 after this point will not be reflected in the copy in /etc/ihcp which is invoked at boot time. Use the ihcp_install-2.6 "autoload" or "all" option to update the copy in /etc/ihcp. To remove the driver module from the kernel, delete the driver object from the modules directory, remove the rc.local entry, remove /etc/ihcp (if empty after removing ihcp_install-2.6*), and delete the device node(s): (as super user) ihcp_install-2.6 remove To remove, compile, load, and enable autoloading of the driver with a single command: (as super user) ihcp_install-2.6 all or (as super user) ihcp_install-2.6 all debug The script will try to determine the appropriate compile include directory: This is somewhat of a moving target. Tested for, in order, are: /lib/modules/`uname -r`/build/include and /usr/src/linux. If no directory in the list is detected, the compiler is allowed to use its default include directory (probably /usr/include). The default directory is usually not suitable for compiling driver modules, and will cause warnings and errors. The install script will allow for multiple boards up to a maximum set by the IHCP_BOARDS variable. The current default is 4. Increase as necessary. The script will only create device nodes for boards actually found (as determined by idr devices found in /proc/interrupts. The device node(s) will be created with owner=bin group=bin and permissions=666. Other script variables that control the default configuration of the driver and board, as well as the behavior of the script itself may be modified. See the Driver Default Parameters section, below, for information on driver "defines". See the script code for script variable information. NOTES: Residual Byte Counts: It is a unix convention that the read and write routines return a count of the number of bytes actually transferred. Unfortunately, the pci interface chip on the 10117 does not have a readable dma range counter. Therefore, the hardcopy driver always indicates that all bytes requested in a write call were transferred. Unless an error was encountered, this will in fact always be the case. Errors will be indicated by the driver returning an error code after a read or write call. In general, bytes lost in an aborted read or write cannot be re-sent successfully anyway, so the "actual" byte count would serve little purpose. IOCTL parameters: The PCI hardcopy driver maintains ioctl() compatibility with Tahoma's SunOS and Solaris drivers for Sbus boards, and with the Solaris and Solaris 7 drivers for Pci boards. It is also compatible with Tahoma's HP-UX drivers. Also defined are a set of commands intended to be compatible with SUN's and Versatec's drivers for Tahoma's model 10088 VMEbus board. This should allow existing printer/plotter applications to run with little or no modification. Note that the ihcp_io.h file for Linux is slightly different from the ihcp_io.h files provided with the drivers for Solaris 1 and 2 and HP-UX. The old and new ....io.h files will generate the same ioctl commands, but are not interchangeable. Use the new xxxx_io.h files when compiling on a Linux machine. NOTE: A call to the 10117 driver ioctl code looks like: ioctl(filedes,cmd,arg); filedes is the file descriptor of the open device, cmd is a command defined in ihcp_io.h, and arg is a pointer. For most ioctls arg will point to a 32 bit variable. IHCP_GET_REGS requires an array of IHCP_RETURN_ARRAY_SIZE 32 bit integers, as in: __u32 argarray[IHCP_RETURN_ARRAY_SIZE]; IHCP_DATA_OUT takes a char array of output data bytes. These variable and array sizes are the same for both 32 and 64 bit applications. Size-invariant data types such as __u32 should be used when defining ioctl arg pointer targets variables. #include "./idr_io.h" will pick up and include the fixed types. Auto Line Terminate ioctls: The Pci Hardcopy boards and driver support auto line terminate when in Versatec mode. When this mode is enabled, raster data can be streamed through the driver and board, and the board will automatically transmit line terminate commands to the plotter based on a programmable count. This can provide additional transfer efficiency when plotting to an area smaller than the plotters full raster width. With this feature, it is not necessary to pad each raster to the full plotter width. Use IHCPIO_AUTO_LTR_COUNT to set the desired count. IHCPIO_AUTO_LTR_ON and _OFF enable and disable the auto ltr functrion. The _ON and _OFF ioctls flow through the fifo with other data and commands, but _COUNT does NOT. The count should not be written when there is any data in the fifo if auto ltr is currently enabled, or has been previously enabled. In the current implementation of the driver, this requirement persists across open() and close(). In order to maintain maximum pipelining, the driver does not wait for fifo empty and device ready in either open() or close(). If the calling program(s) use the auto ltr feature, it may be necessary to use the IHCPIO_RDY_WAIT ioctl after open, and/or before close. Calling this ioctl after open, rather than prior to close will allow maximum overlap between calling program execution, and data transfer to the attached plotter. Device Names: The application code calling the Hardcopy driver may expect a different node name than that created by the install script (vp0, lp0, etc.) if it was originally written to talk to the VME hardcopy driver or Versatec's Sbus driver. It may be necessary to establish a symbolic link between the /dev/... entry that the application expects to see, and the /dev/ihcp0 (or 1, or 2, or ...) that ihcp_install-2.6 creates. Hardcopy Sample Program: Iktest0.c is a (very) simple sample program that calls the hardcopy driver, and prints a few lines of "hello world". This can be used as a simple test, and indicates in general how to use the ioctl call, and how writes are done. Iktest0 sets PRINT mode if VPI strapping is detected. iktest0.c detects the board strapping - Centronics or VPI - and sets print mode if the board is set for VPI. Filtertest: This tiny program is a sample filter that can be used when printing through the hardcopy driver. It will scan its input, looking for new line characters, and will add a carriage return when it finds a new line. This allows printing unix text files to printers that require carriage returns - HP laserjet is one example. To print directly to the hardcopy device, one might do something like: pr | ./filtertest > /dev/ihcp0 Ikonex: This is a command line utility that can be used to exercise the driver/board/plotter combination. With ikonex it is possible to issue commands, send (pre-existing)data files, and read status to/from the attached plotter. It must be compiled in the same directory as ihcp_io.h. Ikonex may be run entirely from a command line, or in interactive mode. It is self- documenting. After compiling, run ./ikonex for further information. Driver Default Properties: Several driver defaults and compile options may be changed by editing ihcp_install-2.6 and reloading the driver. A debug compile options may also be selected as a command line option to the install script. Many of these defaults may also be overridden by application programs, using ioctl() calls. IHCP_BOARDS: The driver init_module code will probe for the number of boards specified by IHCP_BOARDS. The pci bios probe function will be called repeatedly until no further boards are found, or IHCP_BOARDS is exhausted. Specifying a board count larger than the number of boards present will be somewhat wasteful of kernel memory, as each (potential) board requires a kmalloc'd state structure. The install script wil create as many device nodes as specified by IDR_BOARDS, regardless of the number of boards actually found. Node names will be ihcp0, ihcp1, ... The default value is 4. IHCP_VERS_SPEED: IHCP_CENT_SPEED: The handshake speed for Centronics and Versatec compatible plotters may be set separately using the IHCP_VERS_SPEED and IHCP_CENT_SPEED variables, respectively. The available choices for each are 0 through 3, with 0 giving the fastest handshake. Generally speaking, the fastest handshake that gives relaible data transfer should be used. Many Versatec compatible plotters will tolerate speed 0, while most Centronics compatible devices will require speed 1 or slower. The factory defaults are both 1. IHCP_MODE: The load-time default mode for Versatec compatible devices is selected by the IHCP_MODE variable. If it is 0, the board and plotter will default to print mode. If it is 1, plot mode will be selected. The default mode as shipped is plot mode. IHCP_DMA_TIME: IHCP_FIFO_TIME: Two software timers are used in the driver. One controls how long the driver waits for a DMA transfer to complete. The other times the wait for device ready or fifo <1/2 full ioctl. These times are set by IHCP_DMA_TIME, and IHCP_FIFO_TIME, respectively. The appropriate variable should be set equal to the desired timeout value in SECONDS. Some plotters can take a relatively long time to complete a transfer, or a plot, so numbers like 1800 seconds (30 minutes) are not unreasonable. The values as shipped from the factory are 1800 seconds for each timer. IHCP_MAX_PHYS_ORDER: This version of the driver does DMA directly from mapped and pinned user buffer when possible. It will do a maximum of 2**IHCP_MAX_PHYS_ORDER * PAGE_SIZE bytes per DMA transfer. User buffers larger than this value will be transferred in multiple DMA blocks, transparently to the calling program. In the case of a HIGHMEM_64G (but not x86_64) enabled kernel a copy buffer of 2**IHCP_MAX_PHYS_ORDER * PAGE_SIZE bytes will be allocated. Larger DMA transfers will improve overall efficiency somewhat, by minimizing the number of interrupts taken per user buffer (one per DMA block transfer). The tradeoff, of course, is the kernel memory and mapping resources used to map and pin the buffer, and build the scatter-gather list for the board's DMA mechanism. (On non-PC architectures - not tested yet - there will also be increased usage of iommu mapping resources). Given the quantities of cheap memory available on PC's, one should probably just choose a buffer size that keeps the plotter happy. IHCP_MAX_PHYS_ORDER is specified in "orders". That is, 2**IHCP_MAX_PHYS_ORDER * PAGE_SIZE gives the max transfer size in bytes. The default is 4, which with the current 4K page size gives 64k max bytes per transfer. IHCP_BYTE_ORDER: Typically, PCI bus DMA transfers on a PC shold be done low byte first (little endian mode). Some platforms may require transfers to be high byte first (big endian mode). Setting this default parameter to 0 puts the board and driver into little endian mode. A 1 selects big endian mode. The default is 0. Compile Options: IHCP_DEBUG: Causes the driver to emit _many_ diagnostic printfs during operation. Do not use during normal production plotting! IHCP_USER_DMA: IHCP_USER_DMA = AUTO allows the driver to compile in user buffer DMA mode, as long as CONFIG_HIGHMEM_64G is not true. If the kernel supports > 4G of RMA (CONFIG_HIGHMEM_64G true) a copy buffer will be used - except in x86_64 systems. If IHCP_USER_DMA = NO, a copy buffer will always be used. IHCP_FORCE_LOAD: If this option = YES, the -f (force) option will be used when the driver is loaded by insmod. This is probably not a good idea as it will ignore some important driver/kernel compatibility checks. It may be helpful during driver development.