diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/block/acsi_slm.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/block/acsi_slm.c')
-rw-r--r-- | drivers/block/acsi_slm.c | 1045 |
1 files changed, 1045 insertions, 0 deletions
diff --git a/drivers/block/acsi_slm.c b/drivers/block/acsi_slm.c new file mode 100644 index 000000000000..e3be8c31a74c --- /dev/null +++ b/drivers/block/acsi_slm.c @@ -0,0 +1,1045 @@ +/* + * acsi_slm.c -- Device driver for the Atari SLM laser printer + * + * Copyright 1995 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + */ + +/* + +Notes: + +The major number for SLM printers is 28 (like ACSI), but as a character +device, not block device. The minor number is the number of the printer (if +you have more than one SLM; currently max. 2 (#define-constant) SLMs are +supported). The device can be opened for reading and writing. If reading it, +you get some status infos (MODE SENSE data). Writing mode is used for the data +to be printed. Some ioctls allow to get the printer status and to tune printer +modes and some internal variables. + +A special problem of the SLM driver is the timing and thus the buffering of +the print data. The problem is that all the data for one page must be present +in memory when printing starts, else --when swapping occurs-- the timing could +not be guaranteed. There are several ways to assure this: + + 1) Reserve a buffer of 1196k (maximum page size) statically by + atari_stram_alloc(). The data are collected there until they're complete, + and then printing starts. Since the buffer is reserved, no further + considerations about memory and swapping are needed. So this is the + simplest method, but it needs a lot of memory for just the SLM. + + An striking advantage of this method is (supposed the SLM_CONT_CNT_REPROG + method works, see there), that there are no timing problems with the DMA + anymore. + + 2) The other method would be to reserve the buffer dynamically each time + printing is required. I could think of looking at mem_map where the + largest unallocted ST-RAM area is, taking the area, and then extending it + by swapping out the neighbored pages, until the needed size is reached. + This requires some mm hacking, but seems possible. The only obstacle could + be pages that cannot be swapped out (reserved pages)... + + 3) Another possibility would be to leave the real data in user space and to + work with two dribble buffers of about 32k in the driver: While the one + buffer is DMAed to the SLM, the other can be filled with new data. But + to keep the timing, that requires that the user data remain in memory and + are not swapped out. Requires mm hacking, too, but maybe not so bad as + method 2). + +*/ + +#include <linux/module.h> + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/fs.h> +#include <linux/major.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/smp_lock.h> + +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/atarihw.h> +#include <asm/atariints.h> +#include <asm/atari_acsi.h> +#include <asm/atari_stdma.h> +#include <asm/atari_stram.h> +#include <asm/atari_SLM.h> + + +#undef DEBUG + +/* Define this if the page data are continuous in physical memory. That + * requires less reprogramming of the ST-DMA */ +#define SLM_CONTINUOUS_DMA + +/* Use continuous reprogramming of the ST-DMA counter register. This is + * --strictly speaking-- not allowed, Atari recommends not to look at the + * counter register while a DMA is going on. But I don't know if that applies + * only for reading the register, or also writing to it. Writing only works + * fine for me... The advantage is that the timing becomes absolutely + * uncritical: Just update each, say 200ms, the counter reg to its maximum, + * and the DMA will work until the status byte interrupt occurs. + */ +#define SLM_CONT_CNT_REPROG + +#define CMDSET_TARG_LUN(cmd,targ,lun) \ + do { \ + cmd[0] = (cmd[0] & ~0xe0) | (targ)<<5; \ + cmd[1] = (cmd[1] & ~0xe0) | (lun)<<5; \ + } while(0) + +#define START_TIMER(to) mod_timer(&slm_timer, jiffies + (to)) +#define STOP_TIMER() del_timer(&slm_timer) + + +static char slmreqsense_cmd[6] = { 0x03, 0, 0, 0, 0, 0 }; +static char slmprint_cmd[6] = { 0x0a, 0, 0, 0, 0, 0 }; +static char slminquiry_cmd[6] = { 0x12, 0, 0, 0, 0, 0x80 }; +static char slmmsense_cmd[6] = { 0x1a, 0, 0, 0, 255, 0 }; +#if 0 +static char slmmselect_cmd[6] = { 0x15, 0, 0, 0, 0, 0 }; +#endif + + +#define MAX_SLM 2 + +static struct slm { + unsigned target; /* target number */ + unsigned lun; /* LUN in target controller */ + atomic_t wr_ok; /* set to 0 if output part busy */ + atomic_t rd_ok; /* set to 0 if status part busy */ +} slm_info[MAX_SLM]; + +int N_SLM_Printers = 0; + +/* printer buffer */ +static unsigned char *SLMBuffer; /* start of buffer */ +static unsigned char *BufferP; /* current position in buffer */ +static int BufferSize; /* length of buffer for page size */ + +typedef enum { IDLE, FILLING, PRINTING } SLMSTATE; +static SLMSTATE SLMState; +static int SLMBufOwner; /* SLM# currently using the buffer */ + +/* DMA variables */ +#ifndef SLM_CONT_CNT_REPROG +static unsigned long SLMCurAddr; /* current base addr of DMA chunk */ +static unsigned long SLMEndAddr; /* expected end addr */ +static unsigned long SLMSliceSize; /* size of one DMA chunk */ +#endif +static int SLMError; + +/* wait queues */ +static DECLARE_WAIT_QUEUE_HEAD(slm_wait); /* waiting for buffer */ +static DECLARE_WAIT_QUEUE_HEAD(print_wait); /* waiting for printing finished */ + +/* status codes */ +#define SLMSTAT_OK 0x00 +#define SLMSTAT_ORNERY 0x02 +#define SLMSTAT_TONER 0x03 +#define SLMSTAT_WARMUP 0x04 +#define SLMSTAT_PAPER 0x05 +#define SLMSTAT_DRUM 0x06 +#define SLMSTAT_INJAM 0x07 +#define SLMSTAT_THRJAM 0x08 +#define SLMSTAT_OUTJAM 0x09 +#define SLMSTAT_COVER 0x0a +#define SLMSTAT_FUSER 0x0b +#define SLMSTAT_IMAGER 0x0c +#define SLMSTAT_MOTOR 0x0d +#define SLMSTAT_VIDEO 0x0e +#define SLMSTAT_SYSTO 0x10 +#define SLMSTAT_OPCODE 0x12 +#define SLMSTAT_DEVNUM 0x15 +#define SLMSTAT_PARAM 0x1a +#define SLMSTAT_ACSITO 0x1b /* driver defined */ +#define SLMSTAT_NOTALL 0x1c /* driver defined */ + +static char *SLMErrors[] = { + /* 0x00 */ "OK and ready", + /* 0x01 */ NULL, + /* 0x02 */ "ornery printer", + /* 0x03 */ "toner empty", + /* 0x04 */ "warming up", + /* 0x05 */ "paper empty", + /* 0x06 */ "drum empty", + /* 0x07 */ "input jam", + /* 0x08 */ "through jam", + /* 0x09 */ "output jam", + /* 0x0a */ "cover open", + /* 0x0b */ "fuser malfunction", + /* 0x0c */ "imager malfunction", + /* 0x0d */ "motor malfunction", + /* 0x0e */ "video malfunction", + /* 0x0f */ NULL, + /* 0x10 */ "printer system timeout", + /* 0x11 */ NULL, + /* 0x12 */ "invalid operation code", + /* 0x13 */ NULL, + /* 0x14 */ NULL, + /* 0x15 */ "invalid device number", + /* 0x16 */ NULL, + /* 0x17 */ NULL, + /* 0x18 */ NULL, + /* 0x19 */ NULL, + /* 0x1a */ "invalid parameter list", + /* 0x1b */ "ACSI timeout", + /* 0x1c */ "not all printed" +}; + +#define N_ERRORS (sizeof(SLMErrors)/sizeof(*SLMErrors)) + +/* real (driver caused) error? */ +#define IS_REAL_ERROR(x) (x > 0x10) + + +static struct { + char *name; + int w, h; +} StdPageSize[] = { + { "Letter", 2400, 3180 }, + { "Legal", 2400, 4080 }, + { "A4", 2336, 3386 }, + { "B5", 2016, 2914 } +}; + +#define N_STD_SIZES (sizeof(StdPageSize)/sizeof(*StdPageSize)) + +#define SLM_BUFFER_SIZE (2336*3386/8) /* A4 for now */ +#define SLM_DMA_AMOUNT 255 /* #sectors to program the DMA for */ + +#ifdef SLM_CONTINUOUS_DMA +# define SLM_DMA_INT_OFFSET 0 /* DMA goes until seccnt 0, no offs */ +# define SLM_DMA_END_OFFSET 32 /* 32 Byte ST-DMA FIFO */ +# define SLM_SLICE_SIZE(w) (255*512) +#else +# define SLM_DMA_INT_OFFSET 32 /* 32 Byte ST-DMA FIFO */ +# define SLM_DMA_END_OFFSET 32 /* 32 Byte ST-DMA FIFO */ +# define SLM_SLICE_SIZE(w) ((254*512)/(w/8)*(w/8)) +#endif + +/* calculate the number of jiffies to wait for 'n' bytes */ +#ifdef SLM_CONT_CNT_REPROG +#define DMA_TIME_FOR(n) 50 +#define DMA_STARTUP_TIME 0 +#else +#define DMA_TIME_FOR(n) (n/1400-1) +#define DMA_STARTUP_TIME 650 +#endif + +/***************************** Prototypes *****************************/ + +static char *slm_errstr( int stat ); +static int slm_getstats( char *buffer, int device ); +static ssize_t slm_read( struct file* file, char *buf, size_t count, loff_t + *ppos ); +static void start_print( int device ); +static irqreturn_t slm_interrupt(int irc, void *data, struct pt_regs *fp); +static void slm_test_ready( unsigned long dummy ); +static void set_dma_addr( unsigned long paddr ); +static unsigned long get_dma_addr( void ); +static ssize_t slm_write( struct file *file, const char *buf, size_t count, + loff_t *ppos ); +static int slm_ioctl( struct inode *inode, struct file *file, unsigned int + cmd, unsigned long arg ); +static int slm_open( struct inode *inode, struct file *file ); +static int slm_release( struct inode *inode, struct file *file ); +static int slm_req_sense( int device ); +static int slm_mode_sense( int device, char *buffer, int abs_flag ); +#if 0 +static int slm_mode_select( int device, char *buffer, int len, int + default_flag ); +#endif +static int slm_get_pagesize( int device, int *w, int *h ); + +/************************* End of Prototypes **************************/ + + +static struct timer_list slm_timer = TIMER_INITIALIZER(slm_test_ready, 0, 0); + +static struct file_operations slm_fops = { + .owner = THIS_MODULE, + .read = slm_read, + .write = slm_write, + .ioctl = slm_ioctl, + .open = slm_open, + .release = slm_release, +}; + + +/* ---------------------------------------------------------------------- */ +/* Status Functions */ + + +static char *slm_errstr( int stat ) + +{ char *p; + static char str[22]; + + stat &= 0x1f; + if (stat >= 0 && stat < N_ERRORS && (p = SLMErrors[stat])) + return( p ); + sprintf( str, "unknown status 0x%02x", stat ); + return( str ); +} + + +static int slm_getstats( char *buffer, int device ) + +{ int len = 0, stat, i, w, h; + unsigned char buf[256]; + + stat = slm_mode_sense( device, buf, 0 ); + if (IS_REAL_ERROR(stat)) + return( -EIO ); + +#define SHORTDATA(i) ((buf[i] << 8) | buf[i+1]) +#define BOOLDATA(i,mask) ((buf[i] & mask) ? "on" : "off") + + w = SHORTDATA( 3 ); + h = SHORTDATA( 1 ); + + len += sprintf( buffer+len, "Status\t\t%s\n", + slm_errstr( stat ) ); + len += sprintf( buffer+len, "Page Size\t%dx%d", + w, h ); + + for( i = 0; i < N_STD_SIZES; ++i ) { + if (w == StdPageSize[i].w && h == StdPageSize[i].h) + break; + } + if (i < N_STD_SIZES) + len += sprintf( buffer+len, " (%s)", StdPageSize[i].name ); + buffer[len++] = '\n'; + + len += sprintf( buffer+len, "Top/Left Margin\t%d/%d\n", + SHORTDATA( 5 ), SHORTDATA( 7 ) ); + len += sprintf( buffer+len, "Manual Feed\t%s\n", + BOOLDATA( 9, 0x01 ) ); + len += sprintf( buffer+len, "Input Select\t%d\n", + (buf[9] >> 1) & 7 ); + len += sprintf( buffer+len, "Auto Select\t%s\n", + BOOLDATA( 9, 0x10 ) ); + len += sprintf( buffer+len, "Prefeed Paper\t%s\n", + BOOLDATA( 9, 0x20 ) ); + len += sprintf( buffer+len, "Thick Pixels\t%s\n", + BOOLDATA( 9, 0x40 ) ); + len += sprintf( buffer+len, "H/V Resol.\t%d/%d dpi\n", + SHORTDATA( 12 ), SHORTDATA( 10 ) ); + len += sprintf( buffer+len, "System Timeout\t%d\n", + buf[14] ); + len += sprintf( buffer+len, "Scan Time\t%d\n", + SHORTDATA( 15 ) ); + len += sprintf( buffer+len, "Page Count\t%d\n", + SHORTDATA( 17 ) ); + len += sprintf( buffer+len, "In/Out Cap.\t%d/%d\n", + SHORTDATA( 19 ), SHORTDATA( 21 ) ); + len += sprintf( buffer+len, "Stagger Output\t%s\n", + BOOLDATA( 23, 0x01 ) ); + len += sprintf( buffer+len, "Output Select\t%d\n", + (buf[23] >> 1) & 7 ); + len += sprintf( buffer+len, "Duplex Print\t%s\n", + BOOLDATA( 23, 0x10 ) ); + len += sprintf( buffer+len, "Color Sep.\t%s\n", + BOOLDATA( 23, 0x20 ) ); + + return( len ); +} + + +static ssize_t slm_read( struct file *file, char *buf, size_t count, + loff_t *ppos ) + +{ + struct inode *node = file->f_dentry->d_inode; + unsigned long page; + int length; + int end; + + if (count < 0) + return( -EINVAL ); + if (!(page = __get_free_page( GFP_KERNEL ))) + return( -ENOMEM ); + + length = slm_getstats( (char *)page, iminor(node) ); + if (length < 0) { + count = length; + goto out; + } + if (file->f_pos >= length) { + count = 0; + goto out; + } + if (count + file->f_pos > length) + count = length - file->f_pos; + end = count + file->f_pos; + if (copy_to_user(buf, (char *)page + file->f_pos, count)) { + count = -EFAULT; + goto out; + } + file->f_pos = end; +out: free_page( page ); + return( count ); +} + + +/* ---------------------------------------------------------------------- */ +/* Printing */ + + +static void start_print( int device ) + +{ struct slm *sip = &slm_info[device]; + unsigned char *cmd; + unsigned long paddr; + int i; + + stdma_lock( slm_interrupt, NULL ); + + CMDSET_TARG_LUN( slmprint_cmd, sip->target, sip->lun ); + cmd = slmprint_cmd; + paddr = virt_to_phys( SLMBuffer ); + dma_cache_maintenance( paddr, virt_to_phys(BufferP)-paddr, 1 ); + DISABLE_IRQ(); + + /* Low on A1 */ + dma_wd.dma_mode_status = 0x88; + MFPDELAY(); + + /* send the command bytes except the last */ + for( i = 0; i < 5; ++i ) { + DMA_LONG_WRITE( *cmd++, 0x8a ); + udelay(20); + if (!acsi_wait_for_IRQ( HZ/2 )) { + SLMError = 1; + return; /* timeout */ + } + } + /* last command byte */ + DMA_LONG_WRITE( *cmd++, 0x82 ); + MFPDELAY(); + /* set DMA address */ + set_dma_addr( paddr ); + /* program DMA for write and select sector counter reg */ + dma_wd.dma_mode_status = 0x192; + MFPDELAY(); + /* program for 255*512 bytes and start DMA */ + DMA_LONG_WRITE( SLM_DMA_AMOUNT, 0x112 ); + +#ifndef SLM_CONT_CNT_REPROG + SLMCurAddr = paddr; + SLMEndAddr = paddr + SLMSliceSize + SLM_DMA_INT_OFFSET; +#endif + START_TIMER( DMA_STARTUP_TIME + DMA_TIME_FOR( SLMSliceSize )); +#if !defined(SLM_CONT_CNT_REPROG) && defined(DEBUG) + printk( "SLM: CurAddr=%#lx EndAddr=%#lx timer=%ld\n", + SLMCurAddr, SLMEndAddr, DMA_TIME_FOR( SLMSliceSize ) ); +#endif + + ENABLE_IRQ(); +} + + +/* Only called when an error happened or at the end of a page */ + +static irqreturn_t slm_interrupt(int irc, void *data, struct pt_regs *fp) + +{ unsigned long addr; + int stat; + + STOP_TIMER(); + addr = get_dma_addr(); + stat = acsi_getstatus(); + SLMError = (stat < 0) ? SLMSTAT_ACSITO : + (addr < virt_to_phys(BufferP)) ? SLMSTAT_NOTALL : + stat; + + dma_wd.dma_mode_status = 0x80; + MFPDELAY(); +#ifdef DEBUG + printk( "SLM: interrupt, addr=%#lx, error=%d\n", addr, SLMError ); +#endif + + wake_up( &print_wait ); + stdma_release(); + ENABLE_IRQ(); + return IRQ_HANDLED; +} + + +static void slm_test_ready( unsigned long dummy ) + +{ +#ifdef SLM_CONT_CNT_REPROG + /* program for 255*512 bytes again */ + dma_wd.fdc_acces_seccount = SLM_DMA_AMOUNT; + START_TIMER( DMA_TIME_FOR(0) ); +#ifdef DEBUG + printk( "SLM: reprogramming timer for %d jiffies, addr=%#lx\n", + DMA_TIME_FOR(0), get_dma_addr() ); +#endif + +#else /* !SLM_CONT_CNT_REPROG */ + + unsigned long flags, addr; + int d, ti; +#ifdef DEBUG + struct timeval start_tm, end_tm; + int did_wait = 0; +#endif + + local_irq_save(flags); + + addr = get_dma_addr(); + if ((d = SLMEndAddr - addr) > 0) { + local_irq_restore(flags); + + /* slice not yet finished, decide whether to start another timer or to + * busy-wait */ + ti = DMA_TIME_FOR( d ); + if (ti > 0) { +#ifdef DEBUG + printk( "SLM: reprogramming timer for %d jiffies, rest %d bytes\n", + ti, d ); +#endif + START_TIMER( ti ); + return; + } + /* wait for desired end address to be reached */ +#ifdef DEBUG + do_gettimeofday( &start_tm ); + did_wait = 1; +#endif + local_irq_disable(); + while( get_dma_addr() < SLMEndAddr ) + barrier(); + } + + /* slice finished, start next one */ + SLMCurAddr += SLMSliceSize; + +#ifdef SLM_CONTINUOUS_DMA + /* program for 255*512 bytes again */ + dma_wd.fdc_acces_seccount = SLM_DMA_AMOUNT; +#else + /* set DMA address; + * add 2 bytes for the ones in the SLM controller FIFO! */ + set_dma_addr( SLMCurAddr + 2 ); + /* toggle DMA to write and select sector counter reg */ + dma_wd.dma_mode_status = 0x92; + MFPDELAY(); + dma_wd.dma_mode_status = 0x192; + MFPDELAY(); + /* program for 255*512 bytes and start DMA */ + DMA_LONG_WRITE( SLM_DMA_AMOUNT, 0x112 ); +#endif + + local_irq_restore(flags); + +#ifdef DEBUG + if (did_wait) { + int ms; + do_gettimeofday( &end_tm ); + ms = (end_tm.tv_sec*1000000+end_tm.tv_usec) - + (start_tm.tv_sec*1000000+start_tm.tv_usec); + printk( "SLM: did %ld.%ld ms busy waiting for %d bytes\n", + ms/1000, ms%1000, d ); + } + else + printk( "SLM: didn't wait (!)\n" ); +#endif + + if ((unsigned char *)PTOV( SLMCurAddr + SLMSliceSize ) >= BufferP) { + /* will be last slice, no timer necessary */ +#ifdef DEBUG + printk( "SLM: CurAddr=%#lx EndAddr=%#lx last slice -> no timer\n", + SLMCurAddr, SLMEndAddr ); +#endif + } + else { + /* not last slice */ + SLMEndAddr = SLMCurAddr + SLMSliceSize + SLM_DMA_INT_OFFSET; + START_TIMER( DMA_TIME_FOR( SLMSliceSize )); +#ifdef DEBUG + printk( "SLM: CurAddr=%#lx EndAddr=%#lx timer=%ld\n", + SLMCurAddr, SLMEndAddr, DMA_TIME_FOR( SLMSliceSize ) ); +#endif + } +#endif /* SLM_CONT_CNT_REPROG */ +} + + +static void set_dma_addr( unsigned long paddr ) + +{ unsigned long flags; + + local_irq_save(flags); + dma_wd.dma_lo = (unsigned char)paddr; + paddr >>= 8; + MFPDELAY(); + dma_wd.dma_md = (unsigned char)paddr; + paddr >>= 8; + MFPDELAY(); + if (ATARIHW_PRESENT( EXTD_DMA )) + st_dma_ext_dmahi = (unsigned short)paddr; + else + dma_wd.dma_hi = (unsigned char)paddr; + MFPDELAY(); + local_irq_restore(flags); +} + + +static unsigned long get_dma_addr( void ) + +{ unsigned long addr; + + addr = dma_wd.dma_lo & 0xff; + MFPDELAY(); + addr |= (dma_wd.dma_md & 0xff) << 8; + MFPDELAY(); + addr |= (dma_wd.dma_hi & 0xff) << 16; + MFPDELAY(); + + return( addr ); +} + + +static ssize_t slm_write( struct file *file, const char *buf, size_t count, + loff_t *ppos ) + +{ + struct inode *node = file->f_dentry->d_inode; + int device = iminor(node); + int n, filled, w, h; + + while( SLMState == PRINTING || + (SLMState == FILLING && SLMBufOwner != device) ) { + interruptible_sleep_on( &slm_wait ); + if (signal_pending(current)) + return( -ERESTARTSYS ); + } + if (SLMState == IDLE) { + /* first data of page: get current page size */ + if (slm_get_pagesize( device, &w, &h )) + return( -EIO ); + BufferSize = w*h/8; + if (BufferSize > SLM_BUFFER_SIZE) + return( -ENOMEM ); + + SLMState = FILLING; + SLMBufOwner = device; + } + + n = count; + filled = BufferP - SLMBuffer; + if (filled + n > BufferSize) + n = BufferSize - filled; + + if (copy_from_user(BufferP, buf, n)) + return -EFAULT; + BufferP += n; + filled += n; + + if (filled == BufferSize) { + /* Check the paper size again! The user may have switched it in the + * time between starting the data and finishing them. Would end up in + * a trashy page... */ + if (slm_get_pagesize( device, &w, &h )) + return( -EIO ); + if (BufferSize != w*h/8) { + printk( KERN_NOTICE "slm%d: page size changed while printing\n", + device ); + return( -EAGAIN ); + } + + SLMState = PRINTING; + /* choose a slice size that is a multiple of the line size */ +#ifndef SLM_CONT_CNT_REPROG + SLMSliceSize = SLM_SLICE_SIZE(w); +#endif + + start_print( device ); + sleep_on( &print_wait ); + if (SLMError && IS_REAL_ERROR(SLMError)) { + printk( KERN_ERR "slm%d: %s\n", device, slm_errstr(SLMError) ); + n = -EIO; + } + + SLMState = IDLE; + BufferP = SLMBuffer; + wake_up_interruptible( &slm_wait ); + } + + return( n ); +} + + +/* ---------------------------------------------------------------------- */ +/* ioctl Functions */ + + +static int slm_ioctl( struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg ) + +{ int device = iminor(inode), err; + + /* I can think of setting: + * - manual feed + * - paper format + * - copy count + * - ... + * but haven't implemented that yet :-) + * BTW, has anybody better docs about the MODE SENSE/MODE SELECT data? + */ + switch( cmd ) { + + case SLMIORESET: /* reset buffer, i.e. empty the buffer */ + if (!(file->f_mode & 2)) + return( -EINVAL ); + if (SLMState == PRINTING) + return( -EBUSY ); + SLMState = IDLE; + BufferP = SLMBuffer; + wake_up_interruptible( &slm_wait ); + return( 0 ); + + case SLMIOGSTAT: { /* get status */ + int stat; + char *str; + + stat = slm_req_sense( device ); + if (arg) { + str = slm_errstr( stat ); + if (put_user(stat, + (long *)&((struct SLM_status *)arg)->stat)) + return -EFAULT; + if (copy_to_user( ((struct SLM_status *)arg)->str, str, + strlen(str) + 1)) + return -EFAULT; + } + return( stat ); + } + + case SLMIOGPSIZE: { /* get paper size */ + int w, h; + + if ((err = slm_get_pagesize( device, &w, &h ))) return( err ); + + if (put_user(w, (long *)&((struct SLM_paper_size *)arg)->width)) + return -EFAULT; + if (put_user(h, (long *)&((struct SLM_paper_size *)arg)->height)) + return -EFAULT; + return( 0 ); + } + + case SLMIOGMFEED: /* get manual feed */ + return( -EINVAL ); + + case SLMIOSPSIZE: /* set paper size */ + return( -EINVAL ); + + case SLMIOSMFEED: /* set manual feed */ + return( -EINVAL ); + + } + return( -EINVAL ); +} + + +/* ---------------------------------------------------------------------- */ +/* Opening and Closing */ + + +static int slm_open( struct inode *inode, struct file *file ) + +{ int device; + struct slm *sip; + + device = iminor(inode); + if (device >= N_SLM_Printers) + return( -ENXIO ); + sip = &slm_info[device]; + + if (file->f_mode & 2) { + /* open for writing is exclusive */ + if ( !atomic_dec_and_test(&sip->wr_ok) ) { + atomic_inc(&sip->wr_ok); + return( -EBUSY ); + } + } + if (file->f_mode & 1) { + /* open for reading is exclusive */ + if ( !atomic_dec_and_test(&sip->rd_ok) ) { + atomic_inc(&sip->rd_ok); + return( -EBUSY ); + } + } + + return( 0 ); +} + + +static int slm_release( struct inode *inode, struct file *file ) + +{ int device; + struct slm *sip; + + device = iminor(inode); + sip = &slm_info[device]; + + if (file->f_mode & 2) + atomic_inc( &sip->wr_ok ); + if (file->f_mode & 1) + atomic_inc( &sip->rd_ok ); + + return( 0 ); +} + + +/* ---------------------------------------------------------------------- */ +/* ACSI Primitives for the SLM */ + + +static int slm_req_sense( int device ) + +{ int stat, rv; + struct slm *sip = &slm_info[device]; + + stdma_lock( NULL, NULL ); + + CMDSET_TARG_LUN( slmreqsense_cmd, sip->target, sip->lun ); + if (!acsicmd_nodma( slmreqsense_cmd, 0 ) || + (stat = acsi_getstatus()) < 0) + rv = SLMSTAT_ACSITO; + else + rv = stat & 0x1f; + + ENABLE_IRQ(); + stdma_release(); + return( rv ); +} + + +static int slm_mode_sense( int device, char *buffer, int abs_flag ) + +{ unsigned char stat, len; + int rv = 0; + struct slm *sip = &slm_info[device]; + + stdma_lock( NULL, NULL ); + + CMDSET_TARG_LUN( slmmsense_cmd, sip->target, sip->lun ); + slmmsense_cmd[5] = abs_flag ? 0x80 : 0; + if (!acsicmd_nodma( slmmsense_cmd, 0 )) { + rv = SLMSTAT_ACSITO; + goto the_end; + } + + if (!acsi_extstatus( &stat, 1 )) { + acsi_end_extstatus(); + rv = SLMSTAT_ACSITO; + goto the_end; + } + + if (!acsi_extstatus( &len, 1 )) { + acsi_end_extstatus(); + rv = SLMSTAT_ACSITO; + goto the_end; + } + buffer[0] = len; + if (!acsi_extstatus( buffer+1, len )) { + acsi_end_extstatus(); + rv = SLMSTAT_ACSITO; + goto the_end; + } + + acsi_end_extstatus(); + rv = stat & 0x1f; + + the_end: + ENABLE_IRQ(); + stdma_release(); + return( rv ); +} + + +#if 0 +/* currently unused */ +static int slm_mode_select( int device, char *buffer, int len, + int default_flag ) + +{ int stat, rv; + struct slm *sip = &slm_info[device]; + + stdma_lock( NULL, NULL ); + + CMDSET_TARG_LUN( slmmselect_cmd, sip->target, sip->lun ); + slmmselect_cmd[5] = default_flag ? 0x80 : 0; + if (!acsicmd_nodma( slmmselect_cmd, 0 )) { + rv = SLMSTAT_ACSITO; + goto the_end; + } + + if (!default_flag) { + unsigned char c = len; + if (!acsi_extcmd( &c, 1 )) { + rv = SLMSTAT_ACSITO; + goto the_end; + } + if (!acsi_extcmd( buffer, len )) { + rv = SLMSTAT_ACSITO; + goto the_end; + } + } + + stat = acsi_getstatus(); + rv = (stat < 0 ? SLMSTAT_ACSITO : stat); + + the_end: + ENABLE_IRQ(); + stdma_release(); + return( rv ); +} +#endif + + +static int slm_get_pagesize( int device, int *w, int *h ) + +{ char buf[256]; + int stat; + + stat = slm_mode_sense( device, buf, 0 ); + ENABLE_IRQ(); + stdma_release(); + + if (stat != SLMSTAT_OK) + return( -EIO ); + + *w = (buf[3] << 8) | buf[4]; + *h = (buf[1] << 8) | buf[2]; + return( 0 ); +} + + +/* ---------------------------------------------------------------------- */ +/* Initialization */ + + +int attach_slm( int target, int lun ) + +{ static int did_register; + int len; + + if (N_SLM_Printers >= MAX_SLM) { + printk( KERN_WARNING "Too much SLMs\n" ); + return( 0 ); + } + + /* do an INQUIRY */ + udelay(100); + CMDSET_TARG_LUN( slminquiry_cmd, target, lun ); + if (!acsicmd_nodma( slminquiry_cmd, 0 )) { + inq_timeout: + printk( KERN_ERR "SLM inquiry command timed out.\n" ); + inq_fail: + acsi_end_extstatus(); + return( 0 ); + } + /* read status and header of return data */ + if (!acsi_extstatus( SLMBuffer, 6 )) + goto inq_timeout; + + if (SLMBuffer[1] != 2) { /* device type == printer? */ + printk( KERN_ERR "SLM inquiry returned device type != printer\n" ); + goto inq_fail; + } + len = SLMBuffer[5]; + + /* read id string */ + if (!acsi_extstatus( SLMBuffer, len )) + goto inq_timeout; + acsi_end_extstatus(); + SLMBuffer[len] = 0; + + if (!did_register) { + did_register = 1; + } + + slm_info[N_SLM_Printers].target = target; + slm_info[N_SLM_Printers].lun = lun; + atomic_set(&slm_info[N_SLM_Printers].wr_ok, 1 ); + atomic_set(&slm_info[N_SLM_Printers].rd_ok, 1 ); + + printk( KERN_INFO " Printer: %s\n", SLMBuffer ); + printk( KERN_INFO "Detected slm%d at id %d lun %d\n", + N_SLM_Printers, target, lun ); + N_SLM_Printers++; + return( 1 ); +} + +int slm_init( void ) + +{ + int i; + if (register_chrdev( ACSI_MAJOR, "slm", &slm_fops )) { + printk( KERN_ERR "Unable to get major %d for ACSI SLM\n", ACSI_MAJOR ); + return -EBUSY; + } + + if (!(SLMBuffer = atari_stram_alloc( SLM_BUFFER_SIZE, "SLM" ))) { + printk( KERN_ERR "Unable to get SLM ST-Ram buffer.\n" ); + unregister_chrdev( ACSI_MAJOR, "slm" ); + return -ENOMEM; + } + BufferP = SLMBuffer; + SLMState = IDLE; + + devfs_mk_dir("slm"); + for (i = 0; i < MAX_SLM; i++) { + devfs_mk_cdev(MKDEV(ACSI_MAJOR, i), + S_IFCHR|S_IRUSR|S_IWUSR, "slm/%d", i); + } + return 0; +} + +#ifdef MODULE + +/* from acsi.c */ +void acsi_attach_SLMs( int (*attach_func)( int, int ) ); + +int init_module(void) +{ + int err; + + if ((err = slm_init())) + return( err ); + /* This calls attach_slm() for every target/lun where acsi.c detected a + * printer */ + acsi_attach_SLMs( attach_slm ); + return( 0 ); +} + +void cleanup_module(void) +{ + int i; + for (i = 0; i < MAX_SLM; i++) + devfs_remove("slm/%d", i); + devfs_remove("slm"); + if (unregister_chrdev( ACSI_MAJOR, "slm" ) != 0) + printk( KERN_ERR "acsi_slm: cleanup_module failed\n"); + atari_stram_free( SLMBuffer ); +} +#endif |