Import 2.1.33
[davej-history.git] / drivers / char / lp.c
blobe87de84b06a064814bbc3c844a804d7ad1075eaa
1 /*
2 * Copyright (C) 1992 by Jim Weigand and Linus Torvalds
3 * Copyright (C) 1992,1993 by Michael K. Johnson
4 * - Thanks much to Gunter Windau for pointing out to me where the error
5 * checking ought to be.
6 * Copyright (C) 1993 by Nigel Gamble (added interrupt code)
7 * Copyright (C) 1994 by Alan Cox (Modularised it)
8 * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
9 * Statistics and support for slow printers by Rob Janssen, rob@knoware.nl
10 * "lp=" command line parameters added by Grant Guenther, grant@torque.net
11 * lp_read (Status readback) support added by Carsten Gross,
12 * carsten@sol.wohnheim.uni-ulm.de
15 #include <linux/module.h>
17 #include <linux/errno.h>
18 #include <linux/kernel.h>
19 #include <linux/major.h>
20 #include <linux/sched.h>
21 #include <linux/malloc.h>
22 #include <linux/ioport.h>
23 #include <linux/fcntl.h>
24 #include <linux/delay.h>
26 #include <asm/io.h>
27 #include <asm/uaccess.h>
28 #include <asm/system.h>
29 #include <linux/parport.h>
30 #include <linux/lp.h>
32 /* the BIOS manuals say there can be up to 4 lpt devices
33 * but I have not seen a board where the 4th address is listed
34 * if you have different hardware change the table below
35 * please let me know if you have different equipment
36 * if you have more than 3 printers, remember to increase LP_NO
38 struct lp_struct lp_table[] =
40 {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
41 {0}},
42 {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
43 {0}},
44 {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
45 {0}}
47 #define LP_NO 3
49 /* Device name */
50 static char *dev_name = "lp";
52 /* Test if printer is ready (and optionally has no error conditions) */
53 #define LP_READY(minor, status) \
54 ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY))
55 #define LP_CAREFUL_READY(minor, status) \
56 ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : 1)
57 #define _LP_CAREFUL_READY(status) \
58 (status & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
59 (LP_PBUSY|LP_PSELECD|LP_PERRORP)
61 #undef LP_DEBUG
62 #undef LP_READ_DEBUG
64 static int lp_reset(int minor)
66 w_ctr(minor, LP_PSELECP);
67 udelay(LP_DELAY);
68 w_ctr(minor, LP_PSELECP | LP_PINITP);
69 return r_str(minor);
72 static inline int lp_char_polled(char lpchar, int minor)
74 int status, wait = 0;
75 unsigned long count = 0;
76 struct lp_stats *stats;
78 do {
79 status = r_str(minor);
80 count++;
81 if (need_resched)
82 schedule();
83 } while (!LP_READY(minor, status) && count < LP_CHAR(minor));
85 if (count == LP_CHAR(minor)) {
86 return 0;
87 /* we timed out, and the character was /not/ printed */
89 w_dtr(minor, lpchar);
90 stats = &LP_STAT(minor);
91 stats->chars++;
92 /* must wait before taking strobe high, and after taking strobe
93 low, according spec. Some printers need it, others don't. */
94 while (wait != LP_WAIT(minor))
95 wait++;
96 /* control port takes strobe high */
97 w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
98 while (wait)
99 wait--;
100 /* take strobe low */
101 w_ctr(minor, LP_PSELECP | LP_PINITP);
102 /* update waittime statistics */
103 if (count > stats->maxwait) {
104 #ifdef LP_DEBUG
105 printk(KERN_DEBUG "lp%d success after %d counts.\n", minor, count);
106 #endif
107 stats->maxwait = count;
109 count *= 256;
110 wait = (count > stats->meanwait) ? count - stats->meanwait :
111 stats->meanwait - count;
112 stats->meanwait = (255 * stats->meanwait + count + 128) / 256;
113 stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
115 return 1;
118 static inline int lp_char_interrupt(char lpchar, int minor)
120 int wait;
121 unsigned long count = 0;
122 unsigned char status;
123 struct lp_stats *stats;
125 do {
126 if(need_resched)
127 schedule();
128 if ((status = r_str(minor)) & LP_PBUSY) {
129 if (!LP_CAREFUL_READY(minor, status))
130 return 0;
131 w_dtr(minor, lpchar);
132 stats = &LP_STAT(minor);
133 stats->chars++;
134 /* must wait before taking strobe high, and after taking strobe
135 low, according spec. Some printers need it, others don't. */
136 wait = 0;
137 while (wait != LP_WAIT(minor))
138 wait++;
139 /* control port takes strobe high */
140 w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
141 while (wait)
142 wait--;
143 /* take strobe low */
144 w_ctr(minor, LP_PSELECP | LP_PINITP);
145 /* update waittime statistics */
146 if (count) {
147 if (count > stats->maxwait)
148 stats->maxwait = count;
149 count *= 256;
150 wait = (count > stats->meanwait) ? count - stats->meanwait :
151 stats->meanwait - count;
152 stats->meanwait = (255 * stats->meanwait + count + 128) / 256;
153 stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
155 return 1;
157 } while (count++ < LP_CHAR(minor));
159 return 0;
162 static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
164 struct parport *pb = (struct parport *) dev_id;
165 struct ppd *pd = pb->cad;
166 struct lp_struct *lp_dev = (struct lp_struct *) pd->private;
168 if (lp_dev->lp_wait_q)
169 wake_up(&lp_dev->lp_wait_q);
172 static inline int lp_write_interrupt(unsigned int minor, const char *buf, int count)
174 unsigned long copy_size;
175 unsigned long total_bytes_written = 0;
176 unsigned long bytes_written;
177 struct lp_struct *lp = &lp_table[minor];
178 unsigned char status;
180 if (minor >= LP_NO)
181 return -ENXIO;
182 if (lp_table[minor].dev == NULL)
183 return -ENXIO;
185 do {
186 bytes_written = 0;
187 copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
188 copy_from_user(lp->lp_buffer, buf, copy_size);
190 while (copy_size) {
191 if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
192 --copy_size;
193 ++bytes_written;
194 lp_table[minor].runchars++;
195 } else {
196 int rc = total_bytes_written + bytes_written;
197 if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
198 LP_STAT(minor).maxrun = lp_table[minor].runchars;
199 status = r_str(minor);
200 if ((status & LP_POUTPA)) {
201 printk(KERN_INFO "lp%d out of paper\n", minor);
202 if (LP_F(minor) & LP_ABORT)
203 return rc ? rc : -ENOSPC;
204 } else if (!(status & LP_PSELECD)) {
205 printk(KERN_INFO "lp%d off-line\n", minor);
206 if (LP_F(minor) & LP_ABORT)
207 return rc ? rc : -EIO;
208 } else if (!(status & LP_PERRORP)) {
209 printk(KERN_ERR "lp%d printer error\n", minor);
210 if (LP_F(minor) & LP_ABORT)
211 return rc ? rc : -EIO;
213 LP_STAT(minor).sleeps++;
214 cli();
215 w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
216 status = r_str(minor);
217 if ((!(status & LP_PACK) || (status & LP_PBUSY))
218 && LP_CAREFUL_READY(minor, status)) {
219 w_ctr(minor, LP_PSELECP | LP_PINITP);
220 sti();
221 continue;
223 lp_table[minor].runchars = 0;
224 current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
225 interruptible_sleep_on(&lp->lp_wait_q);
226 w_ctr(minor, LP_PSELECP | LP_PINITP);
227 sti();
228 if (current->signal & ~current->blocked) {
229 if (total_bytes_written + bytes_written)
230 return total_bytes_written + bytes_written;
231 else
232 return -EINTR;
237 total_bytes_written += bytes_written;
238 buf += bytes_written;
239 count -= bytes_written;
241 } while (count > 0);
243 return total_bytes_written;
246 static inline int lp_write_polled(unsigned int minor, const char *buf, int count)
248 int retval, status;
249 char c;
250 const char *temp;
252 temp = buf;
253 while (count > 0) {
254 get_user(c, temp);
255 retval = lp_char_polled(c, minor);
256 /* only update counting vars if character was printed */
257 if (retval) {
258 count--; temp++;
259 lp_table[minor].runchars++;
260 } else { /* if printer timed out */
261 if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
262 LP_STAT(minor).maxrun = lp_table[minor].runchars;
263 status = r_str(minor);
265 if (status & LP_POUTPA) {
266 printk(KERN_INFO "lp%d out of paper\n", minor);
267 if(LP_F(minor) & LP_ABORT)
268 return temp-buf?temp-buf:-ENOSPC;
269 current->state = TASK_INTERRUPTIBLE;
270 current->timeout = jiffies + LP_TIMEOUT_POLLED;
271 schedule();
272 } else
273 if (!(status & LP_PSELECD)) {
274 printk(KERN_INFO "lp%d off-line\n", minor);
275 if(LP_F(minor) & LP_ABORT)
276 return temp-buf?temp-buf:-EIO;
277 current->state = TASK_INTERRUPTIBLE;
278 current->timeout = jiffies + LP_TIMEOUT_POLLED;
279 schedule();
280 } else
281 /* not offline or out of paper. on fire? */
282 if (!(status & LP_PERRORP)) {
283 printk(KERN_ERR "lp%d on fire\n", minor);
284 if(LP_F(minor) & LP_ABORT)
285 return temp-buf?temp-buf:-EIO;
286 current->state = TASK_INTERRUPTIBLE;
287 current->timeout = jiffies + LP_TIMEOUT_POLLED;
288 schedule();
291 /* check for signals before going to sleep */
292 if (current->signal & ~current->blocked) {
293 if (temp != buf)
294 return temp-buf;
295 else
296 return -EINTR;
298 LP_STAT(minor).sleeps++;
299 #ifdef LP_DEBUG
300 printk(KERN_DEBUG "lp%d sleeping at %d characters for %d jiffies\n",
301 minor,lp_table[minor].runchars, LP_TIME(minor));
302 #endif
303 lp_table[minor].runchars=0;
304 current->state = TASK_INTERRUPTIBLE;
305 current->timeout = jiffies + LP_TIME(minor);
306 schedule();
309 return temp-buf;
312 static long lp_write(struct inode * inode, struct file * file,
313 const char * buf, unsigned long count)
315 unsigned int minor = MINOR(inode->i_rdev);
316 int retv;
318 if (jiffies-lp_table[minor].lastcall > LP_TIME(minor))
319 lp_table[minor].runchars = 0;
321 lp_table[minor].lastcall = jiffies;
323 /* Claim Parport or sleep until it becomes available
324 * (see lp_wakeup() for details)
326 if (parport_claim(lp_table[minor].dev)) {
327 sleep_on(&lp_table[minor].lp_wait_q);
328 lp_table[minor].lp_wait_q = NULL;
330 if (LP_IRQ(minor) > 0)
331 retv = lp_write_interrupt(minor, buf, count);
332 else
333 retv = lp_write_polled(minor, buf, count);
335 parport_release(lp_table[minor].dev);
336 return retv;
339 static long long lp_lseek(struct inode * inode, struct file * file,
340 long long offset, int origin)
342 return -ESPIPE;
345 #ifdef CONFIG_PRINTER_READBACK
347 static int lp_read_nibble(int minor)
349 unsigned char i;
350 i=r_str(minor)>>3;
351 i&=~8;
352 if ( ( i & 0x10) == 0) i|=8;
353 return(i & 0x0f);
356 static void lp_select_in_high(int minor) {
357 w_ctr(minor, (r_ctr(minor) | 8));
360 /* Status readback confirming to ieee1284 */
361 static long lp_read(struct inode * inode, struct file * file,
362 char * buf, unsigned long count)
364 unsigned char z=0, Byte=0, status;
365 char *temp;
366 int retval;
367 unsigned int counter=0;
368 unsigned int i;
369 unsigned int minor=MINOR(inode->i_rdev);
371 /* Claim Parport or sleep until it becomes available
372 * (see lp_wakeup() for details)
374 if (parport_claim(lp_table[minor].dev)) {
375 sleep_on(&lp_table[minor].lp_wait_q);
376 lp_table[minor].lp_wait_q = NULL;
379 temp=buf;
380 #ifdef LP_READ_DEBUG
381 printk(KERN_INFO "lp%d: read mode\n", minor);
382 #endif
384 retval = verify_area(VERIFY_WRITE, buf, count);
385 if (retval)
386 return retval;
387 if (parport_ieee1284_nibble_mode_ok(lp_table[minor].dev->port, 0)==0) {
388 #ifdef LP_READ_DEBUG
389 printk(KERN_INFO "lp%d: rejected IEEE1284 negotiation.\n",
390 minor);
391 #endif
392 lp_select_in_high(minor);
393 parport_release(lp_table[minor].dev);
394 return temp-buf; /* End of file */
396 for (i=0; i<=(count*2); i++) {
397 w_ctr(minor, r_ctr(minor) | 2); /* AutoFeed high */
398 do {
399 status=(r_str(minor) & 0x40);
400 udelay(50);
401 counter++;
402 if (need_resched)
403 schedule();
404 } while ( (status == 0x40) && (counter < 20) );
405 if ( counter == 20 ) { /* Timeout */
406 #ifdef LP_READ_DEBUG
407 printk(KERN_DEBUG "lp_read: (Autofeed high) timeout\n");
408 #endif
409 w_ctr(minor, r_ctr(minor) & ~2);
410 lp_select_in_high(minor);
411 parport_release(lp_table[minor].dev);
412 return temp-buf; /* end the read at timeout */
414 counter=0;
415 z=lp_read_nibble(minor);
416 w_ctr(minor, r_ctr(minor) & ~2); /* AutoFeed low */
417 do {
418 status=(r_str(minor) & 0x40);
419 udelay(20);
420 counter++;
421 if (need_resched)
422 schedule();
423 } while ( (status == 0) && (counter < 20) );
424 if (counter == 20) { /* Timeout */
425 #ifdef LP_READ_DEBUG
426 printk(KERN_DEBUG "lp_read: (Autofeed low) timeout\n");
427 #endif
428 if (current->signal & ~current->blocked) {
429 lp_select_in_high(minor);
430 parport_release(lp_table[minor].dev);
431 if (temp !=buf)
432 return temp-buf;
433 else
434 return -EINTR;
436 current->state=TASK_INTERRUPTIBLE;
437 current->timeout=jiffies + LP_TIME(minor);
438 schedule();
440 counter=0;
441 if (( i & 1) != 0) {
442 Byte= (Byte | z<<4);
443 put_user(Byte, temp);
444 temp++;
445 } else Byte=z;
447 lp_select_in_high(minor);
448 parport_release(lp_table[minor].dev);
449 return temp-buf;
452 #endif
454 static int lp_open(struct inode * inode, struct file * file)
456 unsigned int minor = MINOR(inode->i_rdev);
458 if (minor >= LP_NO)
459 return -ENXIO;
460 if ((LP_F(minor) & LP_EXIST) == 0)
461 return -ENXIO;
462 if (LP_F(minor) & LP_BUSY)
463 return -EBUSY;
465 MOD_INC_USE_COUNT;
467 /* If ABORTOPEN is set and the printer is offline or out of paper,
468 we may still want to open it to perform ioctl()s. Therefore we
469 have commandeered O_NONBLOCK, even though it is being used in
470 a non-standard manner. This is strictly a Linux hack, and
471 should most likely only ever be used by the tunelp application. */
472 if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
473 int status = r_str(minor);
474 if (status & LP_POUTPA) {
475 printk(KERN_INFO "lp%d out of paper\n", minor);
476 MOD_DEC_USE_COUNT;
477 return -ENOSPC;
478 } else if (!(status & LP_PSELECD)) {
479 printk(KERN_INFO "lp%d off-line\n", minor);
480 MOD_DEC_USE_COUNT;
481 return -EIO;
482 } else if (!(status & LP_PERRORP)) {
483 printk(KERN_ERR "lp%d printer error\n", minor);
484 MOD_DEC_USE_COUNT;
485 return -EIO;
488 if (LP_IRQ(minor) > 0) {
489 lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
490 if (!lp_table[minor].lp_buffer) {
491 MOD_DEC_USE_COUNT;
492 return -ENOMEM;
495 LP_F(minor) |= LP_BUSY;
496 return 0;
499 static int lp_release(struct inode * inode, struct file * file)
501 unsigned int minor = MINOR(inode->i_rdev);
502 unsigned int irq;
504 if ((irq = LP_IRQ(minor))) {
505 kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
506 lp_table[minor].lp_buffer = NULL;
508 LP_F(minor) &= ~LP_BUSY;
509 MOD_DEC_USE_COUNT;
510 return 0;
514 static int lp_ioctl(struct inode *inode, struct file *file,
515 unsigned int cmd, unsigned long arg)
517 unsigned int minor = MINOR(inode->i_rdev);
518 int retval = 0;
520 #ifdef LP_DEBUG
521 printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
522 #endif
523 if (minor >= LP_NO)
524 return -ENODEV;
525 if ((LP_F(minor) & LP_EXIST) == 0)
526 return -ENODEV;
527 switch ( cmd ) {
528 case LPTIME:
529 LP_TIME(minor) = arg * HZ/100;
530 break;
531 case LPCHAR:
532 LP_CHAR(minor) = arg;
533 break;
534 case LPABORT:
535 if (arg)
536 LP_F(minor) |= LP_ABORT;
537 else
538 LP_F(minor) &= ~LP_ABORT;
539 break;
540 case LPABORTOPEN:
541 if (arg)
542 LP_F(minor) |= LP_ABORTOPEN;
543 else
544 LP_F(minor) &= ~LP_ABORTOPEN;
545 break;
546 case LPCAREFUL:
547 if (arg)
548 LP_F(minor) |= LP_CAREFUL;
549 else
550 LP_F(minor) &= ~LP_CAREFUL;
551 break;
552 case LPWAIT:
553 LP_WAIT(minor) = arg;
554 break;
555 case LPSETIRQ:
556 return -EINVAL;
557 break;
558 case LPGETIRQ:
559 retval = verify_area(VERIFY_WRITE, (void *) arg,
560 sizeof(int));
561 if (retval)
562 return retval;
563 copy_to_user((int *) arg, &LP_IRQ(minor), sizeof(int));
564 break;
565 case LPGETSTATUS:
566 retval = verify_area(VERIFY_WRITE, (void *) arg,
567 sizeof(int));
568 if (retval)
569 return retval;
570 else {
571 int status = r_str(minor);
572 copy_to_user((int *) arg, &status, sizeof(int));
574 break;
575 case LPRESET:
576 lp_reset(minor);
577 break;
578 case LPGETSTATS:
579 retval = verify_area(VERIFY_WRITE, (void *) arg,
580 sizeof(struct lp_stats));
581 if (retval)
582 return retval;
583 else {
584 copy_to_user((int *) arg, &LP_STAT(minor), sizeof(struct lp_stats));
585 if (suser())
586 memset(&LP_STAT(minor), 0, sizeof(struct lp_stats));
588 break;
589 case LPGETFLAGS:
590 retval = verify_area(VERIFY_WRITE, (void *) arg,
591 sizeof(int));
592 if (retval)
593 return retval;
594 else {
595 int status = LP_F(minor);
596 copy_to_user((int *) arg, &status, sizeof(int));
598 break;
599 default:
600 retval = -EINVAL;
602 return retval;
606 static struct file_operations lp_fops = {
607 lp_lseek,
608 #ifdef CONFIG_PRINTER_READBACK
609 lp_read,
610 #else
611 NULL,
612 #endif
613 lp_write,
614 NULL, /* lp_readdir */
615 NULL, /* lp_poll */
616 lp_ioctl,
617 NULL, /* lp_mmap */
618 lp_open,
619 lp_release
622 static int parport[LP_NO] = { -1, };
624 #ifdef MODULE
625 #define lp_init init_module
626 MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "i");
628 #else
630 static int parport_ptr = 0;
632 void lp_setup(char *str, int *ints)
634 /* Ugh. */
635 if (!strncmp(str, "parport", 7)) {
636 int n = simple_strtoul(str+7, NULL, 10);
637 if (parport_ptr < LP_NO)
638 parport[parport_ptr++] = n;
639 else
640 printk(KERN_INFO "lp: too many ports, %s ignored.\n",
641 str);
642 } else if (!strcmp(str, "auto")) {
643 parport[0] = -3;
644 } else {
645 if (ints[0] == 0 || ints[1] == 0) {
646 /* disable driver on "parport=" or "parport=0" */
647 parport[0] = -2;
648 } else {
649 printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", ints[1]);
654 #endif
656 int lp_wakeup(void *ref)
658 struct lp_struct *lp_dev = (struct lp_struct *) ref;
660 if (!lp_dev->lp_wait_q)
661 return 1; /* Wake up whom? */
663 /* Claim the Parport */
664 if (parport_claim(lp_dev->dev))
665 return 1; /* Shouldn't happen */
667 wake_up(&lp_dev->lp_wait_q);
668 return 0;
671 static int inline lp_searchfor(int list[], int a)
673 int i;
674 for (i = 0; i < LP_NO && list[i] != -1; i++) {
675 if (list[i] == a) return 1;
677 return 0;
680 int lp_init(void)
682 int count = 0;
683 struct parport *pb;
685 if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
686 printk("lp: unable to get major %d\n", LP_MAJOR);
687 return -EIO;
690 if (parport[0] == -2) return 0;
692 pb = parport_enumerate();
694 while (pb) {
695 if (parport[0] == -1 || lp_searchfor(parport, count) ||
696 (parport[0] == -3 &&
697 pb->probe_info.class == PARPORT_CLASS_PRINTER)) {
698 lp_table[count].dev =
699 parport_register_device(pb, dev_name, NULL,
700 lp_wakeup,
701 lp_interrupt, PARPORT_DEV_TRAN,
702 (void *) &lp_table[count]);
703 lp_table[count].flags |= LP_EXIST;
704 printk(KERN_INFO "lp%d: using %s at 0x%x, ", count,
705 pb->name, pb->base);
706 if (pb->irq == -1)
707 printk("polling.\n");
708 else
709 printk("irq %d.\n", pb->irq);
711 if (++count == LP_NO)
712 break;
713 pb = pb->next;
716 /* Successful specified devices increase count
717 * Unsuccessful specified devices increase failed
719 if (count)
720 return 0;
722 printk(KERN_INFO "lp: driver loaded but no devices found\n");
723 return 1;
726 #ifdef MODULE
727 void cleanup_module(void)
729 int offset;
731 unregister_chrdev(LP_MAJOR, "lp");
732 for (offset = 0; offset < LP_NO; offset++) {
733 if (lp_table[offset].dev == NULL)
734 continue;
735 parport_unregister_device(lp_table[offset].dev);
738 #endif