diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
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 @@ | |||
1 | /* | ||
2 | * acsi_slm.c -- Device driver for the Atari SLM laser printer | ||
3 | * | ||
4 | * Copyright 1995 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file COPYING in the main directory of this archive for | ||
8 | * more details. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | |||
14 | Notes: | ||
15 | |||
16 | The major number for SLM printers is 28 (like ACSI), but as a character | ||
17 | device, not block device. The minor number is the number of the printer (if | ||
18 | you have more than one SLM; currently max. 2 (#define-constant) SLMs are | ||
19 | supported). The device can be opened for reading and writing. If reading it, | ||
20 | you get some status infos (MODE SENSE data). Writing mode is used for the data | ||
21 | to be printed. Some ioctls allow to get the printer status and to tune printer | ||
22 | modes and some internal variables. | ||
23 | |||
24 | A special problem of the SLM driver is the timing and thus the buffering of | ||
25 | the print data. The problem is that all the data for one page must be present | ||
26 | in memory when printing starts, else --when swapping occurs-- the timing could | ||
27 | not be guaranteed. There are several ways to assure this: | ||
28 | |||
29 | 1) Reserve a buffer of 1196k (maximum page size) statically by | ||
30 | atari_stram_alloc(). The data are collected there until they're complete, | ||
31 | and then printing starts. Since the buffer is reserved, no further | ||
32 | considerations about memory and swapping are needed. So this is the | ||
33 | simplest method, but it needs a lot of memory for just the SLM. | ||
34 | |||
35 | An striking advantage of this method is (supposed the SLM_CONT_CNT_REPROG | ||
36 | method works, see there), that there are no timing problems with the DMA | ||
37 | anymore. | ||
38 | |||
39 | 2) The other method would be to reserve the buffer dynamically each time | ||
40 | printing is required. I could think of looking at mem_map where the | ||
41 | largest unallocted ST-RAM area is, taking the area, and then extending it | ||
42 | by swapping out the neighbored pages, until the needed size is reached. | ||
43 | This requires some mm hacking, but seems possible. The only obstacle could | ||
44 | be pages that cannot be swapped out (reserved pages)... | ||
45 | |||
46 | 3) Another possibility would be to leave the real data in user space and to | ||
47 | work with two dribble buffers of about 32k in the driver: While the one | ||
48 | buffer is DMAed to the SLM, the other can be filled with new data. But | ||
49 | to keep the timing, that requires that the user data remain in memory and | ||
50 | are not swapped out. Requires mm hacking, too, but maybe not so bad as | ||
51 | method 2). | ||
52 | |||
53 | */ | ||
54 | |||
55 | #include <linux/module.h> | ||
56 | |||
57 | #include <linux/errno.h> | ||
58 | #include <linux/sched.h> | ||
59 | #include <linux/timer.h> | ||
60 | #include <linux/fs.h> | ||
61 | #include <linux/major.h> | ||
62 | #include <linux/kernel.h> | ||
63 | #include <linux/delay.h> | ||
64 | #include <linux/interrupt.h> | ||
65 | #include <linux/time.h> | ||
66 | #include <linux/mm.h> | ||
67 | #include <linux/slab.h> | ||
68 | #include <linux/devfs_fs_kernel.h> | ||
69 | #include <linux/smp_lock.h> | ||
70 | |||
71 | #include <asm/pgtable.h> | ||
72 | #include <asm/system.h> | ||
73 | #include <asm/uaccess.h> | ||
74 | #include <asm/atarihw.h> | ||
75 | #include <asm/atariints.h> | ||
76 | #include <asm/atari_acsi.h> | ||
77 | #include <asm/atari_stdma.h> | ||
78 | #include <asm/atari_stram.h> | ||
79 | #include <asm/atari_SLM.h> | ||
80 | |||
81 | |||
82 | #undef DEBUG | ||
83 | |||
84 | /* Define this if the page data are continuous in physical memory. That | ||
85 | * requires less reprogramming of the ST-DMA */ | ||
86 | #define SLM_CONTINUOUS_DMA | ||
87 | |||
88 | /* Use continuous reprogramming of the ST-DMA counter register. This is | ||
89 | * --strictly speaking-- not allowed, Atari recommends not to look at the | ||
90 | * counter register while a DMA is going on. But I don't know if that applies | ||
91 | * only for reading the register, or also writing to it. Writing only works | ||
92 | * fine for me... The advantage is that the timing becomes absolutely | ||
93 | * uncritical: Just update each, say 200ms, the counter reg to its maximum, | ||
94 | * and the DMA will work until the status byte interrupt occurs. | ||
95 | */ | ||
96 | #define SLM_CONT_CNT_REPROG | ||
97 | |||
98 | #define CMDSET_TARG_LUN(cmd,targ,lun) \ | ||
99 | do { \ | ||
100 | cmd[0] = (cmd[0] & ~0xe0) | (targ)<<5; \ | ||
101 | cmd[1] = (cmd[1] & ~0xe0) | (lun)<<5; \ | ||
102 | } while(0) | ||
103 | |||
104 | #define START_TIMER(to) mod_timer(&slm_timer, jiffies + (to)) | ||
105 | #define STOP_TIMER() del_timer(&slm_timer) | ||
106 | |||
107 | |||
108 | static char slmreqsense_cmd[6] = { 0x03, 0, 0, 0, 0, 0 }; | ||
109 | static char slmprint_cmd[6] = { 0x0a, 0, 0, 0, 0, 0 }; | ||
110 | static char slminquiry_cmd[6] = { 0x12, 0, 0, 0, 0, 0x80 }; | ||
111 | static char slmmsense_cmd[6] = { 0x1a, 0, 0, 0, 255, 0 }; | ||
112 | #if 0 | ||
113 | static char slmmselect_cmd[6] = { 0x15, 0, 0, 0, 0, 0 }; | ||
114 | #endif | ||
115 | |||
116 | |||
117 | #define MAX_SLM 2 | ||
118 | |||
119 | static struct slm { | ||
120 | unsigned target; /* target number */ | ||
121 | unsigned lun; /* LUN in target controller */ | ||
122 | atomic_t wr_ok; /* set to 0 if output part busy */ | ||
123 | atomic_t rd_ok; /* set to 0 if status part busy */ | ||
124 | } slm_info[MAX_SLM]; | ||
125 | |||
126 | int N_SLM_Printers = 0; | ||
127 | |||
128 | /* printer buffer */ | ||
129 | static unsigned char *SLMBuffer; /* start of buffer */ | ||
130 | static unsigned char *BufferP; /* current position in buffer */ | ||
131 | static int BufferSize; /* length of buffer for page size */ | ||
132 | |||
133 | typedef enum { IDLE, FILLING, PRINTING } SLMSTATE; | ||
134 | static SLMSTATE SLMState; | ||
135 | static int SLMBufOwner; /* SLM# currently using the buffer */ | ||
136 | |||
137 | /* DMA variables */ | ||
138 | #ifndef SLM_CONT_CNT_REPROG | ||
139 | static unsigned long SLMCurAddr; /* current base addr of DMA chunk */ | ||
140 | static unsigned long SLMEndAddr; /* expected end addr */ | ||
141 | static unsigned long SLMSliceSize; /* size of one DMA chunk */ | ||
142 | #endif | ||
143 | static int SLMError; | ||
144 | |||
145 | /* wait queues */ | ||
146 | static DECLARE_WAIT_QUEUE_HEAD(slm_wait); /* waiting for buffer */ | ||
147 | static DECLARE_WAIT_QUEUE_HEAD(print_wait); /* waiting for printing finished */ | ||
148 | |||
149 | /* status codes */ | ||
150 | #define SLMSTAT_OK 0x00 | ||
151 | #define SLMSTAT_ORNERY 0x02 | ||
152 | #define SLMSTAT_TONER 0x03 | ||
153 | #define SLMSTAT_WARMUP 0x04 | ||
154 | #define SLMSTAT_PAPER 0x05 | ||
155 | #define SLMSTAT_DRUM 0x06 | ||
156 | #define SLMSTAT_INJAM 0x07 | ||
157 | #define SLMSTAT_THRJAM 0x08 | ||
158 | #define SLMSTAT_OUTJAM 0x09 | ||
159 | #define SLMSTAT_COVER 0x0a | ||
160 | #define SLMSTAT_FUSER 0x0b | ||
161 | #define SLMSTAT_IMAGER 0x0c | ||
162 | #define SLMSTAT_MOTOR 0x0d | ||
163 | #define SLMSTAT_VIDEO 0x0e | ||
164 | #define SLMSTAT_SYSTO 0x10 | ||
165 | #define SLMSTAT_OPCODE 0x12 | ||
166 | #define SLMSTAT_DEVNUM 0x15 | ||
167 | #define SLMSTAT_PARAM 0x1a | ||
168 | #define SLMSTAT_ACSITO 0x1b /* driver defined */ | ||
169 | #define SLMSTAT_NOTALL 0x1c /* driver defined */ | ||
170 | |||
171 | static char *SLMErrors[] = { | ||
172 | /* 0x00 */ "OK and ready", | ||
173 | /* 0x01 */ NULL, | ||
174 | /* 0x02 */ "ornery printer", | ||
175 | /* 0x03 */ "toner empty", | ||
176 | /* 0x04 */ "warming up", | ||
177 | /* 0x05 */ "paper empty", | ||
178 | /* 0x06 */ "drum empty", | ||
179 | /* 0x07 */ "input jam", | ||
180 | /* 0x08 */ "through jam", | ||
181 | /* 0x09 */ "output jam", | ||
182 | /* 0x0a */ "cover open", | ||
183 | /* 0x0b */ "fuser malfunction", | ||
184 | /* 0x0c */ "imager malfunction", | ||
185 | /* 0x0d */ "motor malfunction", | ||
186 | /* 0x0e */ "video malfunction", | ||
187 | /* 0x0f */ NULL, | ||
188 | /* 0x10 */ "printer system timeout", | ||
189 | /* 0x11 */ NULL, | ||
190 | /* 0x12 */ "invalid operation code", | ||
191 | /* 0x13 */ NULL, | ||
192 | /* 0x14 */ NULL, | ||
193 | /* 0x15 */ "invalid device number", | ||
194 | /* 0x16 */ NULL, | ||
195 | /* 0x17 */ NULL, | ||
196 | /* 0x18 */ NULL, | ||
197 | /* 0x19 */ NULL, | ||
198 | /* 0x1a */ "invalid parameter list", | ||
199 | /* 0x1b */ "ACSI timeout", | ||
200 | /* 0x1c */ "not all printed" | ||
201 | }; | ||
202 | |||
203 | #define N_ERRORS (sizeof(SLMErrors)/sizeof(*SLMErrors)) | ||
204 | |||
205 | /* real (driver caused) error? */ | ||
206 | #define IS_REAL_ERROR(x) (x > 0x10) | ||
207 | |||
208 | |||
209 | static struct { | ||
210 | char *name; | ||
211 | int w, h; | ||
212 | } StdPageSize[] = { | ||
213 | { "Letter", 2400, 3180 }, | ||
214 | { "Legal", 2400, 4080 }, | ||
215 | { "A4", 2336, 3386 }, | ||
216 | { "B5", 2016, 2914 } | ||
217 | }; | ||
218 | |||
219 | #define N_STD_SIZES (sizeof(StdPageSize)/sizeof(*StdPageSize)) | ||
220 | |||
221 | #define SLM_BUFFER_SIZE (2336*3386/8) /* A4 for now */ | ||
222 | #define SLM_DMA_AMOUNT 255 /* #sectors to program the DMA for */ | ||
223 | |||
224 | #ifdef SLM_CONTINUOUS_DMA | ||
225 | # define SLM_DMA_INT_OFFSET 0 /* DMA goes until seccnt 0, no offs */ | ||
226 | # define SLM_DMA_END_OFFSET 32 /* 32 Byte ST-DMA FIFO */ | ||
227 | # define SLM_SLICE_SIZE(w) (255*512) | ||
228 | #else | ||
229 | # define SLM_DMA_INT_OFFSET 32 /* 32 Byte ST-DMA FIFO */ | ||
230 | # define SLM_DMA_END_OFFSET 32 /* 32 Byte ST-DMA FIFO */ | ||
231 | # define SLM_SLICE_SIZE(w) ((254*512)/(w/8)*(w/8)) | ||
232 | #endif | ||
233 | |||
234 | /* calculate the number of jiffies to wait for 'n' bytes */ | ||
235 | #ifdef SLM_CONT_CNT_REPROG | ||
236 | #define DMA_TIME_FOR(n) 50 | ||
237 | #define DMA_STARTUP_TIME 0 | ||
238 | #else | ||
239 | #define DMA_TIME_FOR(n) (n/1400-1) | ||
240 | #define DMA_STARTUP_TIME 650 | ||
241 | #endif | ||
242 | |||
243 | /***************************** Prototypes *****************************/ | ||
244 | |||
245 | static char *slm_errstr( int stat ); | ||
246 | static int slm_getstats( char *buffer, int device ); | ||
247 | static ssize_t slm_read( struct file* file, char *buf, size_t count, loff_t | ||
248 | *ppos ); | ||
249 | static void start_print( int device ); | ||
250 | static irqreturn_t slm_interrupt(int irc, void *data, struct pt_regs *fp); | ||
251 | static void slm_test_ready( unsigned long dummy ); | ||
252 | static void set_dma_addr( unsigned long paddr ); | ||
253 | static unsigned long get_dma_addr( void ); | ||
254 | static ssize_t slm_write( struct file *file, const char *buf, size_t count, | ||
255 | loff_t *ppos ); | ||
256 | static int slm_ioctl( struct inode *inode, struct file *file, unsigned int | ||
257 | cmd, unsigned long arg ); | ||
258 | static int slm_open( struct inode *inode, struct file *file ); | ||
259 | static int slm_release( struct inode *inode, struct file *file ); | ||
260 | static int slm_req_sense( int device ); | ||
261 | static int slm_mode_sense( int device, char *buffer, int abs_flag ); | ||
262 | #if 0 | ||
263 | static int slm_mode_select( int device, char *buffer, int len, int | ||
264 | default_flag ); | ||
265 | #endif | ||
266 | static int slm_get_pagesize( int device, int *w, int *h ); | ||
267 | |||
268 | /************************* End of Prototypes **************************/ | ||
269 | |||
270 | |||
271 | static struct timer_list slm_timer = TIMER_INITIALIZER(slm_test_ready, 0, 0); | ||
272 | |||
273 | static struct file_operations slm_fops = { | ||
274 | .owner = THIS_MODULE, | ||
275 | .read = slm_read, | ||
276 | .write = slm_write, | ||
277 | .ioctl = slm_ioctl, | ||
278 | .open = slm_open, | ||
279 | .release = slm_release, | ||
280 | }; | ||
281 | |||
282 | |||
283 | /* ---------------------------------------------------------------------- */ | ||
284 | /* Status Functions */ | ||
285 | |||
286 | |||
287 | static char *slm_errstr( int stat ) | ||
288 | |||
289 | { char *p; | ||
290 | static char str[22]; | ||
291 | |||
292 | stat &= 0x1f; | ||
293 | if (stat >= 0 && stat < N_ERRORS && (p = SLMErrors[stat])) | ||
294 | return( p ); | ||
295 | sprintf( str, "unknown status 0x%02x", stat ); | ||
296 | return( str ); | ||
297 | } | ||
298 | |||
299 | |||
300 | static int slm_getstats( char *buffer, int device ) | ||
301 | |||
302 | { int len = 0, stat, i, w, h; | ||
303 | unsigned char buf[256]; | ||
304 | |||
305 | stat = slm_mode_sense( device, buf, 0 ); | ||
306 | if (IS_REAL_ERROR(stat)) | ||
307 | return( -EIO ); | ||
308 | |||
309 | #define SHORTDATA(i) ((buf[i] << 8) | buf[i+1]) | ||
310 | #define BOOLDATA(i,mask) ((buf[i] & mask) ? "on" : "off") | ||
311 | |||
312 | w = SHORTDATA( 3 ); | ||
313 | h = SHORTDATA( 1 ); | ||
314 | |||
315 | len += sprintf( buffer+len, "Status\t\t%s\n", | ||
316 | slm_errstr( stat ) ); | ||
317 | len += sprintf( buffer+len, "Page Size\t%dx%d", | ||
318 | w, h ); | ||
319 | |||
320 | for( i = 0; i < N_STD_SIZES; ++i ) { | ||
321 | if (w == StdPageSize[i].w && h == StdPageSize[i].h) | ||
322 | break; | ||
323 | } | ||
324 | if (i < N_STD_SIZES) | ||
325 | len += sprintf( buffer+len, " (%s)", StdPageSize[i].name ); | ||
326 | buffer[len++] = '\n'; | ||
327 | |||
328 | len += sprintf( buffer+len, "Top/Left Margin\t%d/%d\n", | ||
329 | SHORTDATA( 5 ), SHORTDATA( 7 ) ); | ||
330 | len += sprintf( buffer+len, "Manual Feed\t%s\n", | ||
331 | BOOLDATA( 9, 0x01 ) ); | ||
332 | len += sprintf( buffer+len, "Input Select\t%d\n", | ||
333 | (buf[9] >> 1) & 7 ); | ||
334 | len += sprintf( buffer+len, "Auto Select\t%s\n", | ||
335 | BOOLDATA( 9, 0x10 ) ); | ||
336 | len += sprintf( buffer+len, "Prefeed Paper\t%s\n", | ||
337 | BOOLDATA( 9, 0x20 ) ); | ||
338 | len += sprintf( buffer+len, "Thick Pixels\t%s\n", | ||
339 | BOOLDATA( 9, 0x40 ) ); | ||
340 | len += sprintf( buffer+len, "H/V Resol.\t%d/%d dpi\n", | ||
341 | SHORTDATA( 12 ), SHORTDATA( 10 ) ); | ||
342 | len += sprintf( buffer+len, "System Timeout\t%d\n", | ||
343 | buf[14] ); | ||
344 | len += sprintf( buffer+len, "Scan Time\t%d\n", | ||
345 | SHORTDATA( 15 ) ); | ||
346 | len += sprintf( buffer+len, "Page Count\t%d\n", | ||
347 | SHORTDATA( 17 ) ); | ||
348 | len += sprintf( buffer+len, "In/Out Cap.\t%d/%d\n", | ||
349 | SHORTDATA( 19 ), SHORTDATA( 21 ) ); | ||
350 | len += sprintf( buffer+len, "Stagger Output\t%s\n", | ||
351 | BOOLDATA( 23, 0x01 ) ); | ||
352 | len += sprintf( buffer+len, "Output Select\t%d\n", | ||
353 | (buf[23] >> 1) & 7 ); | ||
354 | len += sprintf( buffer+len, "Duplex Print\t%s\n", | ||
355 | BOOLDATA( 23, 0x10 ) ); | ||
356 | len += sprintf( buffer+len, "Color Sep.\t%s\n", | ||
357 | BOOLDATA( 23, 0x20 ) ); | ||
358 | |||
359 | return( len ); | ||
360 | } | ||
361 | |||
362 | |||
363 | static ssize_t slm_read( struct file *file, char *buf, size_t count, | ||
364 | loff_t *ppos ) | ||
365 | |||
366 | { | ||
367 | struct inode *node = file->f_dentry->d_inode; | ||
368 | unsigned long page; | ||
369 | int length; | ||
370 | int end; | ||
371 | |||
372 | if (count < 0) | ||
373 | return( -EINVAL ); | ||
374 | if (!(page = __get_free_page( GFP_KERNEL ))) | ||
375 | return( -ENOMEM ); | ||
376 | |||
377 | length = slm_getstats( (char *)page, iminor(node) ); | ||
378 | if (length < 0) { | ||
379 | count = length; | ||
380 | goto out; | ||
381 | } | ||
382 | if (file->f_pos >= length) { | ||
383 | count = 0; | ||
384 | goto out; | ||
385 | } | ||
386 | if (count + file->f_pos > length) | ||
387 | count = length - file->f_pos; | ||
388 | end = count + file->f_pos; | ||
389 | if (copy_to_user(buf, (char *)page + file->f_pos, count)) { | ||
390 | count = -EFAULT; | ||
391 | goto out; | ||
392 | } | ||
393 | file->f_pos = end; | ||
394 | out: free_page( page ); | ||
395 | return( count ); | ||
396 | } | ||
397 | |||
398 | |||
399 | /* ---------------------------------------------------------------------- */ | ||
400 | /* Printing */ | ||
401 | |||
402 | |||
403 | static void start_print( int device ) | ||
404 | |||
405 | { struct slm *sip = &slm_info[device]; | ||
406 | unsigned char *cmd; | ||
407 | unsigned long paddr; | ||
408 | int i; | ||
409 | |||
410 | stdma_lock( slm_interrupt, NULL ); | ||
411 | |||
412 | CMDSET_TARG_LUN( slmprint_cmd, sip->target, sip->lun ); | ||
413 | cmd = slmprint_cmd; | ||
414 | paddr = virt_to_phys( SLMBuffer ); | ||
415 | dma_cache_maintenance( paddr, virt_to_phys(BufferP)-paddr, 1 ); | ||
416 | DISABLE_IRQ(); | ||
417 | |||
418 | /* Low on A1 */ | ||
419 | dma_wd.dma_mode_status = 0x88; | ||
420 | MFPDELAY(); | ||
421 | |||
422 | /* send the command bytes except the last */ | ||
423 | for( i = 0; i < 5; ++i ) { | ||
424 | DMA_LONG_WRITE( *cmd++, 0x8a ); | ||
425 | udelay(20); | ||
426 | if (!acsi_wait_for_IRQ( HZ/2 )) { | ||
427 | SLMError = 1; | ||
428 | return; /* timeout */ | ||
429 | } | ||
430 | } | ||
431 | /* last command byte */ | ||
432 | DMA_LONG_WRITE( *cmd++, 0x82 ); | ||
433 | MFPDELAY(); | ||
434 | /* set DMA address */ | ||
435 | set_dma_addr( paddr ); | ||
436 | /* program DMA for write and select sector counter reg */ | ||
437 | dma_wd.dma_mode_status = 0x192; | ||
438 | MFPDELAY(); | ||
439 | /* program for 255*512 bytes and start DMA */ | ||
440 | DMA_LONG_WRITE( SLM_DMA_AMOUNT, 0x112 ); | ||
441 | |||
442 | #ifndef SLM_CONT_CNT_REPROG | ||
443 | SLMCurAddr = paddr; | ||
444 | SLMEndAddr = paddr + SLMSliceSize + SLM_DMA_INT_OFFSET; | ||
445 | #endif | ||
446 | START_TIMER( DMA_STARTUP_TIME + DMA_TIME_FOR( SLMSliceSize )); | ||
447 | #if !defined(SLM_CONT_CNT_REPROG) && defined(DEBUG) | ||
448 | printk( "SLM: CurAddr=%#lx EndAddr=%#lx timer=%ld\n", | ||
449 | SLMCurAddr, SLMEndAddr, DMA_TIME_FOR( SLMSliceSize ) ); | ||
450 | #endif | ||
451 | |||
452 | ENABLE_IRQ(); | ||
453 | } | ||
454 | |||
455 | |||
456 | /* Only called when an error happened or at the end of a page */ | ||
457 | |||
458 | static irqreturn_t slm_interrupt(int irc, void *data, struct pt_regs *fp) | ||
459 | |||
460 | { unsigned long addr; | ||
461 | int stat; | ||
462 | |||
463 | STOP_TIMER(); | ||
464 | addr = get_dma_addr(); | ||
465 | stat = acsi_getstatus(); | ||
466 | SLMError = (stat < 0) ? SLMSTAT_ACSITO : | ||
467 | (addr < virt_to_phys(BufferP)) ? SLMSTAT_NOTALL : | ||
468 | stat; | ||
469 | |||
470 | dma_wd.dma_mode_status = 0x80; | ||
471 | MFPDELAY(); | ||
472 | #ifdef DEBUG | ||
473 | printk( "SLM: interrupt, addr=%#lx, error=%d\n", addr, SLMError ); | ||
474 | #endif | ||
475 | |||
476 | wake_up( &print_wait ); | ||
477 | stdma_release(); | ||
478 | ENABLE_IRQ(); | ||
479 | return IRQ_HANDLED; | ||
480 | } | ||
481 | |||
482 | |||
483 | static void slm_test_ready( unsigned long dummy ) | ||
484 | |||
485 | { | ||
486 | #ifdef SLM_CONT_CNT_REPROG | ||
487 | /* program for 255*512 bytes again */ | ||
488 | dma_wd.fdc_acces_seccount = SLM_DMA_AMOUNT; | ||
489 | START_TIMER( DMA_TIME_FOR(0) ); | ||
490 | #ifdef DEBUG | ||
491 | printk( "SLM: reprogramming timer for %d jiffies, addr=%#lx\n", | ||
492 | DMA_TIME_FOR(0), get_dma_addr() ); | ||
493 | #endif | ||
494 | |||
495 | #else /* !SLM_CONT_CNT_REPROG */ | ||
496 | |||
497 | unsigned long flags, addr; | ||
498 | int d, ti; | ||
499 | #ifdef DEBUG | ||
500 | struct timeval start_tm, end_tm; | ||
501 | int did_wait = 0; | ||
502 | #endif | ||
503 | |||
504 | local_irq_save(flags); | ||
505 | |||
506 | addr = get_dma_addr(); | ||
507 | if ((d = SLMEndAddr - addr) > 0) { | ||
508 | local_irq_restore(flags); | ||
509 | |||
510 | /* slice not yet finished, decide whether to start another timer or to | ||
511 | * busy-wait */ | ||
512 | ti = DMA_TIME_FOR( d ); | ||
513 | if (ti > 0) { | ||
514 | #ifdef DEBUG | ||
515 | printk( "SLM: reprogramming timer for %d jiffies, rest %d bytes\n", | ||
516 | ti, d ); | ||
517 | #endif | ||
518 | START_TIMER( ti ); | ||
519 | return; | ||
520 | } | ||
521 | /* wait for desired end address to be reached */ | ||
522 | #ifdef DEBUG | ||
523 | do_gettimeofday( &start_tm ); | ||
524 | did_wait = 1; | ||
525 | #endif | ||
526 | local_irq_disable(); | ||
527 | while( get_dma_addr() < SLMEndAddr ) | ||
528 | barrier(); | ||
529 | } | ||
530 | |||
531 | /* slice finished, start next one */ | ||
532 | SLMCurAddr += SLMSliceSize; | ||
533 | |||
534 | #ifdef SLM_CONTINUOUS_DMA | ||
535 | /* program for 255*512 bytes again */ | ||
536 | dma_wd.fdc_acces_seccount = SLM_DMA_AMOUNT; | ||
537 | #else | ||
538 | /* set DMA address; | ||
539 | * add 2 bytes for the ones in the SLM controller FIFO! */ | ||
540 | set_dma_addr( SLMCurAddr + 2 ); | ||
541 | /* toggle DMA to write and select sector counter reg */ | ||
542 | dma_wd.dma_mode_status = 0x92; | ||
543 | MFPDELAY(); | ||
544 | dma_wd.dma_mode_status = 0x192; | ||
545 | MFPDELAY(); | ||
546 | /* program for 255*512 bytes and start DMA */ | ||
547 | DMA_LONG_WRITE( SLM_DMA_AMOUNT, 0x112 ); | ||
548 | #endif | ||
549 | |||
550 | local_irq_restore(flags); | ||
551 | |||
552 | #ifdef DEBUG | ||
553 | if (did_wait) { | ||
554 | int ms; | ||
555 | do_gettimeofday( &end_tm ); | ||
556 | ms = (end_tm.tv_sec*1000000+end_tm.tv_usec) - | ||
557 | (start_tm.tv_sec*1000000+start_tm.tv_usec); | ||
558 | printk( "SLM: did %ld.%ld ms busy waiting for %d bytes\n", | ||
559 | ms/1000, ms%1000, d ); | ||
560 | } | ||
561 | else | ||
562 | printk( "SLM: didn't wait (!)\n" ); | ||
563 | #endif | ||
564 | |||
565 | if ((unsigned char *)PTOV( SLMCurAddr + SLMSliceSize ) >= BufferP) { | ||
566 | /* will be last slice, no timer necessary */ | ||
567 | #ifdef DEBUG | ||
568 | printk( "SLM: CurAddr=%#lx EndAddr=%#lx last slice -> no timer\n", | ||
569 | SLMCurAddr, SLMEndAddr ); | ||
570 | #endif | ||
571 | } | ||
572 | else { | ||
573 | /* not last slice */ | ||
574 | SLMEndAddr = SLMCurAddr + SLMSliceSize + SLM_DMA_INT_OFFSET; | ||
575 | START_TIMER( DMA_TIME_FOR( SLMSliceSize )); | ||
576 | #ifdef DEBUG | ||
577 | printk( "SLM: CurAddr=%#lx EndAddr=%#lx timer=%ld\n", | ||
578 | SLMCurAddr, SLMEndAddr, DMA_TIME_FOR( SLMSliceSize ) ); | ||
579 | #endif | ||
580 | } | ||
581 | #endif /* SLM_CONT_CNT_REPROG */ | ||
582 | } | ||
583 | |||
584 | |||
585 | static void set_dma_addr( unsigned long paddr ) | ||
586 | |||
587 | { unsigned long flags; | ||
588 | |||
589 | local_irq_save(flags); | ||
590 | dma_wd.dma_lo = (unsigned char)paddr; | ||
591 | paddr >>= 8; | ||
592 | MFPDELAY(); | ||
593 | dma_wd.dma_md = (unsigned char)paddr; | ||
594 | paddr >>= 8; | ||
595 | MFPDELAY(); | ||
596 | if (ATARIHW_PRESENT( EXTD_DMA )) | ||
597 | st_dma_ext_dmahi = (unsigned short)paddr; | ||
598 | else | ||
599 | dma_wd.dma_hi = (unsigned char)paddr; | ||
600 | MFPDELAY(); | ||
601 | local_irq_restore(flags); | ||
602 | } | ||
603 | |||
604 | |||
605 | static unsigned long get_dma_addr( void ) | ||
606 | |||
607 | { unsigned long addr; | ||
608 | |||
609 | addr = dma_wd.dma_lo & 0xff; | ||
610 | MFPDELAY(); | ||
611 | addr |= (dma_wd.dma_md & 0xff) << 8; | ||
612 | MFPDELAY(); | ||
613 | addr |= (dma_wd.dma_hi & 0xff) << 16; | ||
614 | MFPDELAY(); | ||
615 | |||
616 | return( addr ); | ||
617 | } | ||
618 | |||
619 | |||
620 | static ssize_t slm_write( struct file *file, const char *buf, size_t count, | ||
621 | loff_t *ppos ) | ||
622 | |||
623 | { | ||
624 | struct inode *node = file->f_dentry->d_inode; | ||
625 | int device = iminor(node); | ||
626 | int n, filled, w, h; | ||
627 | |||
628 | while( SLMState == PRINTING || | ||
629 | (SLMState == FILLING && SLMBufOwner != device) ) { | ||
630 | interruptible_sleep_on( &slm_wait ); | ||
631 | if (signal_pending(current)) | ||
632 | return( -ERESTARTSYS ); | ||
633 | } | ||
634 | if (SLMState == IDLE) { | ||
635 | /* first data of page: get current page size */ | ||
636 | if (slm_get_pagesize( device, &w, &h )) | ||
637 | return( -EIO ); | ||
638 | BufferSize = w*h/8; | ||
639 | if (BufferSize > SLM_BUFFER_SIZE) | ||
640 | return( -ENOMEM ); | ||
641 | |||
642 | SLMState = FILLING; | ||
643 | SLMBufOwner = device; | ||
644 | } | ||
645 | |||
646 | n = count; | ||
647 | filled = BufferP - SLMBuffer; | ||
648 | if (filled + n > BufferSize) | ||
649 | n = BufferSize - filled; | ||
650 | |||
651 | if (copy_from_user(BufferP, buf, n)) | ||
652 | return -EFAULT; | ||
653 | BufferP += n; | ||
654 | filled += n; | ||
655 | |||
656 | if (filled == BufferSize) { | ||
657 | /* Check the paper size again! The user may have switched it in the | ||
658 | * time between starting the data and finishing them. Would end up in | ||
659 | * a trashy page... */ | ||
660 | if (slm_get_pagesize( device, &w, &h )) | ||
661 | return( -EIO ); | ||
662 | if (BufferSize != w*h/8) { | ||
663 | printk( KERN_NOTICE "slm%d: page size changed while printing\n", | ||
664 | device ); | ||
665 | return( -EAGAIN ); | ||
666 | } | ||
667 | |||
668 | SLMState = PRINTING; | ||
669 | /* choose a slice size that is a multiple of the line size */ | ||
670 | #ifndef SLM_CONT_CNT_REPROG | ||
671 | SLMSliceSize = SLM_SLICE_SIZE(w); | ||
672 | #endif | ||
673 | |||
674 | start_print( device ); | ||
675 | sleep_on( &print_wait ); | ||
676 | if (SLMError && IS_REAL_ERROR(SLMError)) { | ||
677 | printk( KERN_ERR "slm%d: %s\n", device, slm_errstr(SLMError) ); | ||
678 | n = -EIO; | ||
679 | } | ||
680 | |||
681 | SLMState = IDLE; | ||
682 | BufferP = SLMBuffer; | ||
683 | wake_up_interruptible( &slm_wait ); | ||
684 | } | ||
685 | |||
686 | return( n ); | ||
687 | } | ||
688 | |||
689 | |||
690 | /* ---------------------------------------------------------------------- */ | ||
691 | /* ioctl Functions */ | ||
692 | |||
693 | |||
694 | static int slm_ioctl( struct inode *inode, struct file *file, | ||
695 | unsigned int cmd, unsigned long arg ) | ||
696 | |||
697 | { int device = iminor(inode), err; | ||
698 | |||
699 | /* I can think of setting: | ||
700 | * - manual feed | ||
701 | * - paper format | ||
702 | * - copy count | ||
703 | * - ... | ||
704 | * but haven't implemented that yet :-) | ||
705 | * BTW, has anybody better docs about the MODE SENSE/MODE SELECT data? | ||
706 | */ | ||
707 | switch( cmd ) { | ||
708 | |||
709 | case SLMIORESET: /* reset buffer, i.e. empty the buffer */ | ||
710 | if (!(file->f_mode & 2)) | ||
711 | return( -EINVAL ); | ||
712 | if (SLMState == PRINTING) | ||
713 | return( -EBUSY ); | ||
714 | SLMState = IDLE; | ||
715 | BufferP = SLMBuffer; | ||
716 | wake_up_interruptible( &slm_wait ); | ||
717 | return( 0 ); | ||
718 | |||
719 | case SLMIOGSTAT: { /* get status */ | ||
720 | int stat; | ||
721 | char *str; | ||
722 | |||
723 | stat = slm_req_sense( device ); | ||
724 | if (arg) { | ||
725 | str = slm_errstr( stat ); | ||
726 | if (put_user(stat, | ||
727 | (long *)&((struct SLM_status *)arg)->stat)) | ||
728 | return -EFAULT; | ||
729 | if (copy_to_user( ((struct SLM_status *)arg)->str, str, | ||
730 | strlen(str) + 1)) | ||
731 | return -EFAULT; | ||
732 | } | ||
733 | return( stat ); | ||
734 | } | ||
735 | |||
736 | case SLMIOGPSIZE: { /* get paper size */ | ||
737 | int w, h; | ||
738 | |||
739 | if ((err = slm_get_pagesize( device, &w, &h ))) return( err ); | ||
740 | |||
741 | if (put_user(w, (long *)&((struct SLM_paper_size *)arg)->width)) | ||
742 | return -EFAULT; | ||
743 | if (put_user(h, (long *)&((struct SLM_paper_size *)arg)->height)) | ||
744 | return -EFAULT; | ||
745 | return( 0 ); | ||
746 | } | ||
747 | |||
748 | case SLMIOGMFEED: /* get manual feed */ | ||
749 | return( -EINVAL ); | ||
750 | |||
751 | case SLMIOSPSIZE: /* set paper size */ | ||
752 | return( -EINVAL ); | ||
753 | |||
754 | case SLMIOSMFEED: /* set manual feed */ | ||
755 | return( -EINVAL ); | ||
756 | |||
757 | } | ||
758 | return( -EINVAL ); | ||
759 | } | ||
760 | |||
761 | |||
762 | /* ---------------------------------------------------------------------- */ | ||
763 | /* Opening and Closing */ | ||
764 | |||
765 | |||
766 | static int slm_open( struct inode *inode, struct file *file ) | ||
767 | |||
768 | { int device; | ||
769 | struct slm *sip; | ||
770 | |||
771 | device = iminor(inode); | ||
772 | if (device >= N_SLM_Printers) | ||
773 | return( -ENXIO ); | ||
774 | sip = &slm_info[device]; | ||
775 | |||
776 | if (file->f_mode & 2) { | ||
777 | /* open for writing is exclusive */ | ||
778 | if ( !atomic_dec_and_test(&sip->wr_ok) ) { | ||
779 | atomic_inc(&sip->wr_ok); | ||
780 | return( -EBUSY ); | ||
781 | } | ||
782 | } | ||
783 | if (file->f_mode & 1) { | ||
784 | /* open for reading is exclusive */ | ||
785 | if ( !atomic_dec_and_test(&sip->rd_ok) ) { | ||
786 | atomic_inc(&sip->rd_ok); | ||
787 | return( -EBUSY ); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | return( 0 ); | ||
792 | } | ||
793 | |||
794 | |||
795 | static int slm_release( struct inode *inode, struct file *file ) | ||
796 | |||
797 | { int device; | ||
798 | struct slm *sip; | ||
799 | |||
800 | device = iminor(inode); | ||
801 | sip = &slm_info[device]; | ||
802 | |||
803 | if (file->f_mode & 2) | ||
804 | atomic_inc( &sip->wr_ok ); | ||
805 | if (file->f_mode & 1) | ||
806 | atomic_inc( &sip->rd_ok ); | ||
807 | |||
808 | return( 0 ); | ||
809 | } | ||
810 | |||
811 | |||
812 | /* ---------------------------------------------------------------------- */ | ||
813 | /* ACSI Primitives for the SLM */ | ||
814 | |||
815 | |||
816 | static int slm_req_sense( int device ) | ||
817 | |||
818 | { int stat, rv; | ||
819 | struct slm *sip = &slm_info[device]; | ||
820 | |||
821 | stdma_lock( NULL, NULL ); | ||
822 | |||
823 | CMDSET_TARG_LUN( slmreqsense_cmd, sip->target, sip->lun ); | ||
824 | if (!acsicmd_nodma( slmreqsense_cmd, 0 ) || | ||
825 | (stat = acsi_getstatus()) < 0) | ||
826 | rv = SLMSTAT_ACSITO; | ||
827 | else | ||
828 | rv = stat & 0x1f; | ||
829 | |||
830 | ENABLE_IRQ(); | ||
831 | stdma_release(); | ||
832 | return( rv ); | ||
833 | } | ||
834 | |||
835 | |||
836 | static int slm_mode_sense( int device, char *buffer, int abs_flag ) | ||
837 | |||
838 | { unsigned char stat, len; | ||
839 | int rv = 0; | ||
840 | struct slm *sip = &slm_info[device]; | ||
841 | |||
842 | stdma_lock( NULL, NULL ); | ||
843 | |||
844 | CMDSET_TARG_LUN( slmmsense_cmd, sip->target, sip->lun ); | ||
845 | slmmsense_cmd[5] = abs_flag ? 0x80 : 0; | ||
846 | if (!acsicmd_nodma( slmmsense_cmd, 0 )) { | ||
847 | rv = SLMSTAT_ACSITO; | ||
848 | goto the_end; | ||
849 | } | ||
850 | |||
851 | if (!acsi_extstatus( &stat, 1 )) { | ||
852 | acsi_end_extstatus(); | ||
853 | rv = SLMSTAT_ACSITO; | ||
854 | goto the_end; | ||
855 | } | ||
856 | |||
857 | if (!acsi_extstatus( &len, 1 )) { | ||
858 | acsi_end_extstatus(); | ||
859 | rv = SLMSTAT_ACSITO; | ||
860 | goto the_end; | ||
861 | } | ||
862 | buffer[0] = len; | ||
863 | if (!acsi_extstatus( buffer+1, len )) { | ||
864 | acsi_end_extstatus(); | ||
865 | rv = SLMSTAT_ACSITO; | ||
866 | goto the_end; | ||
867 | } | ||
868 | |||
869 | acsi_end_extstatus(); | ||
870 | rv = stat & 0x1f; | ||
871 | |||
872 | the_end: | ||
873 | ENABLE_IRQ(); | ||
874 | stdma_release(); | ||
875 | return( rv ); | ||
876 | } | ||
877 | |||
878 | |||
879 | #if 0 | ||
880 | /* currently unused */ | ||
881 | static int slm_mode_select( int device, char *buffer, int len, | ||
882 | int default_flag ) | ||
883 | |||
884 | { int stat, rv; | ||
885 | struct slm *sip = &slm_info[device]; | ||
886 | |||
887 | stdma_lock( NULL, NULL ); | ||
888 | |||
889 | CMDSET_TARG_LUN( slmmselect_cmd, sip->target, sip->lun ); | ||
890 | slmmselect_cmd[5] = default_flag ? 0x80 : 0; | ||
891 | if (!acsicmd_nodma( slmmselect_cmd, 0 )) { | ||
892 | rv = SLMSTAT_ACSITO; | ||
893 | goto the_end; | ||
894 | } | ||
895 | |||
896 | if (!default_flag) { | ||
897 | unsigned char c = len; | ||
898 | if (!acsi_extcmd( &c, 1 )) { | ||
899 | rv = SLMSTAT_ACSITO; | ||
900 | goto the_end; | ||
901 | } | ||
902 | if (!acsi_extcmd( buffer, len )) { | ||
903 | rv = SLMSTAT_ACSITO; | ||
904 | goto the_end; | ||
905 | } | ||
906 | } | ||
907 | |||
908 | stat = acsi_getstatus(); | ||
909 | rv = (stat < 0 ? SLMSTAT_ACSITO : stat); | ||
910 | |||
911 | the_end: | ||
912 | ENABLE_IRQ(); | ||
913 | stdma_release(); | ||
914 | return( rv ); | ||
915 | } | ||
916 | #endif | ||
917 | |||
918 | |||
919 | static int slm_get_pagesize( int device, int *w, int *h ) | ||
920 | |||
921 | { char buf[256]; | ||
922 | int stat; | ||
923 | |||
924 | stat = slm_mode_sense( device, buf, 0 ); | ||
925 | ENABLE_IRQ(); | ||
926 | stdma_release(); | ||
927 | |||
928 | if (stat != SLMSTAT_OK) | ||
929 | return( -EIO ); | ||
930 | |||
931 | *w = (buf[3] << 8) | buf[4]; | ||
932 | *h = (buf[1] << 8) | buf[2]; | ||
933 | return( 0 ); | ||
934 | } | ||
935 | |||
936 | |||
937 | /* ---------------------------------------------------------------------- */ | ||
938 | /* Initialization */ | ||
939 | |||
940 | |||
941 | int attach_slm( int target, int lun ) | ||
942 | |||
943 | { static int did_register; | ||
944 | int len; | ||
945 | |||
946 | if (N_SLM_Printers >= MAX_SLM) { | ||
947 | printk( KERN_WARNING "Too much SLMs\n" ); | ||
948 | return( 0 ); | ||
949 | } | ||
950 | |||
951 | /* do an INQUIRY */ | ||
952 | udelay(100); | ||
953 | CMDSET_TARG_LUN( slminquiry_cmd, target, lun ); | ||
954 | if (!acsicmd_nodma( slminquiry_cmd, 0 )) { | ||
955 | inq_timeout: | ||
956 | printk( KERN_ERR "SLM inquiry command timed out.\n" ); | ||
957 | inq_fail: | ||
958 | acsi_end_extstatus(); | ||
959 | return( 0 ); | ||
960 | } | ||
961 | /* read status and header of return data */ | ||
962 | if (!acsi_extstatus( SLMBuffer, 6 )) | ||
963 | goto inq_timeout; | ||
964 | |||
965 | if (SLMBuffer[1] != 2) { /* device type == printer? */ | ||
966 | printk( KERN_ERR "SLM inquiry returned device type != printer\n" ); | ||
967 | goto inq_fail; | ||
968 | } | ||
969 | len = SLMBuffer[5]; | ||
970 | |||
971 | /* read id string */ | ||
972 | if (!acsi_extstatus( SLMBuffer, len )) | ||
973 | goto inq_timeout; | ||
974 | acsi_end_extstatus(); | ||
975 | SLMBuffer[len] = 0; | ||
976 | |||
977 | if (!did_register) { | ||
978 | did_register = 1; | ||
979 | } | ||
980 | |||
981 | slm_info[N_SLM_Printers].target = target; | ||
982 | slm_info[N_SLM_Printers].lun = lun; | ||
983 | atomic_set(&slm_info[N_SLM_Printers].wr_ok, 1 ); | ||
984 | atomic_set(&slm_info[N_SLM_Printers].rd_ok, 1 ); | ||
985 | |||
986 | printk( KERN_INFO " Printer: %s\n", SLMBuffer ); | ||
987 | printk( KERN_INFO "Detected slm%d at id %d lun %d\n", | ||
988 | N_SLM_Printers, target, lun ); | ||
989 | N_SLM_Printers++; | ||
990 | return( 1 ); | ||
991 | } | ||
992 | |||
993 | int slm_init( void ) | ||
994 | |||
995 | { | ||
996 | int i; | ||
997 | if (register_chrdev( ACSI_MAJOR, "slm", &slm_fops )) { | ||
998 | printk( KERN_ERR "Unable to get major %d for ACSI SLM\n", ACSI_MAJOR ); | ||
999 | return -EBUSY; | ||
1000 | } | ||
1001 | |||
1002 | if (!(SLMBuffer = atari_stram_alloc( SLM_BUFFER_SIZE, "SLM" ))) { | ||
1003 | printk( KERN_ERR "Unable to get SLM ST-Ram buffer.\n" ); | ||
1004 | unregister_chrdev( ACSI_MAJOR, "slm" ); | ||
1005 | return -ENOMEM; | ||
1006 | } | ||
1007 | BufferP = SLMBuffer; | ||
1008 | SLMState = IDLE; | ||
1009 | |||
1010 | devfs_mk_dir("slm"); | ||
1011 | for (i = 0; i < MAX_SLM; i++) { | ||
1012 | devfs_mk_cdev(MKDEV(ACSI_MAJOR, i), | ||
1013 | S_IFCHR|S_IRUSR|S_IWUSR, "slm/%d", i); | ||
1014 | } | ||
1015 | return 0; | ||
1016 | } | ||
1017 | |||
1018 | #ifdef MODULE | ||
1019 | |||
1020 | /* from acsi.c */ | ||
1021 | void acsi_attach_SLMs( int (*attach_func)( int, int ) ); | ||
1022 | |||
1023 | int init_module(void) | ||
1024 | { | ||
1025 | int err; | ||
1026 | |||
1027 | if ((err = slm_init())) | ||
1028 | return( err ); | ||
1029 | /* This calls attach_slm() for every target/lun where acsi.c detected a | ||
1030 | * printer */ | ||
1031 | acsi_attach_SLMs( attach_slm ); | ||
1032 | return( 0 ); | ||
1033 | } | ||
1034 | |||
1035 | void cleanup_module(void) | ||
1036 | { | ||
1037 | int i; | ||
1038 | for (i = 0; i < MAX_SLM; i++) | ||
1039 | devfs_remove("slm/%d", i); | ||
1040 | devfs_remove("slm"); | ||
1041 | if (unregister_chrdev( ACSI_MAJOR, "slm" ) != 0) | ||
1042 | printk( KERN_ERR "acsi_slm: cleanup_module failed\n"); | ||
1043 | atari_stram_free( SLMBuffer ); | ||
1044 | } | ||
1045 | #endif | ||