Programação de Sistemas Controlador de impressora por interface paralela Programação de Sistemas Centronics : 1/33 Introdução (1) • A interface paralela IEEE1284, ou Centronics, permite dois equipamentos trocar informação de 8 bits em simultâneo. – Usa ficha DB25 – Velocidades de transferência até 150Kbps, com cabo até 10m. – Pinos distribuídos por: • • • • 8 bidireccionais para porto OUTPUT (RW) 5 para porto STATUS(R) 4 para porto CONTROL(RW) 8 ligados ao GND Programação de Sistemas Centronics : 2/33 Introdução (2) • Os 3 portos ocupam os seguintes endereços – Endereço BASE: Output – Endereço BASE+1: Status – Endereço BASE+2: Control • IEEE1284 especifica 4 modos de operação – Compatível: modo básico, assíncrono. – Nibble: do periférico para anfitrião (“host”) em mensagens de 4 bits e a metade da velocidade. – Byte: do periférico para anfitrião em mensagens de 8 bits. (combinado com modo compatível faz o porto ser designado por Bidireccional) – EPP, ECP: conexão half-duplex. Programação de Sistemas Centronics : 3/33 Introdução (3) Ligado ao IRQ7 Status register Bit Pino Sinal Significado 3 15 Error/ Perifério em erro 4 13 Select Perifério seleccionado 5 12 PaperOut Sem papel 6 10 Ack/ Aceitou dados (mantém LOW durante ~ 5µs) 7 11 Busy Perifério ocupado Control register Bit Pino Sinal 0 1 Strobe/ Dados disponíveis (manter LOW durante min 5µs) 1 14 AutoLineFeed/ Ordem para executar automaticamente salto de linha 2 16 Initialize/ Ordem para inicializar (manter LOW durante min 5µs) 3 17 SelectPrinter/ Seleccionar o periférico Programação de Sistemas Significado Centronics : 4/33 Introdução (4) • O controlador vai interagir com impressora em modo E/S programada (“pooling”). • Diagrama temporal de envio de um Byte para o periférico. Programação de Sistemas Centronics : 5/33 Introdução (5) • Uso do porto paralelo para controlar um step motor Programação de Sistemas Centronics : 6/33 Impressora (1) • Pretende-se implementar um controlador para uma impressora HP LaserJet 6. • Impressora de baixo custo, 600 dpi, velocidade 4 páginas/min, alimentador pouco fiável, com interface paralela. • Aceita apenas a descrição de páginas em PCL-Printer Command Language. • Entradas a implementar: open, release e write. Programação de Sistemas Centronics : 7/33 Impressora (2) • Comandos da impressora expressos na linguagem PJLPrinter Job Language da HP. • Uma tarefa de impressão segue a seguinte estrutura <ESC>%-12345X Comandos controlo da tarefa <ESC>E Página 1 … Página n <ESC>E <ESC>%-12345X Programação de Sistemas Centronics : 8/33 Impressora (3) • Consultar manual PCL em http://h20000.www2.hp.com/bc//docs/support/SupportManual/bpl13210/bpl13210.pdf • A linguagem de descrição das páginas PCL-Printer Command Language, da HP, é implementada por todos os fabricantes de impressoras. • Outra linguagem de descrição de páginas, bastante divulgada, é o POSTSCRIPT. • <ESC>%-12345X é designado por UEL-Universal Exit Language, delimitando todas as tarefas Programação de Sistemas Centronics : 9/33 Impressora (4) • Os comandos PCL possuem o formato <ESC>CparameterizadoCgrupo#Cterminação Opcionalmente mais dois campos, Cparâmetro# quantidade • O sistema de coordenadas é definido pela figura • O texto é impresso apenas dentro das margens (tudo o que saia é eliminado) Programação de Sistemas Margens X (0,0) Y Centronics : 10/33 Impressora (5) • Comandos PCL de controlo de página: exemplos ### tamanho página (para A4, #=26) ### fonte papel (para manual, #=2) ### orientação papel (para vertical, #=0) <ESC>&l#A <ESC>&l#H <ESC>&l#O • Fontes determinadas por parâmetros na seguinte ordem: 1. 2. 3. 4. 5. 6. 7. Conjunto de símbolos (PC-8,…) Espaçamento ocupado por cada caractere (igual ou distinto) Pitch (número de caracteres impressos numa polegada horizontal) Altura (diferença em pontos=1/72 polegadas) Estilo (postura-ex:itálico, largura e estrutura-ex:sombreado) Espessura (“stroke weight”) Tipo de letra (Courier,CG Times,…) Programação de Sistemas Hp Centronics : 11/33 Impressora (6) <ESC>(0U<ESC>(s1p14v0s3b4101T ASCII CG Times altura espaçamento proporcional • Comandos PCL de posicionamento <ESC>*p#x#Y ### posiciona cursor na linha, coluna • Terminação de linha <ESC>&k#G Programação de Sistemas ### para #=2, LF substituído por CR+LF Centronics : 12/33 Interface • Funções a implementar: int init_module(void) void cleanup_module(void) int parport_open(struct inode *inode, struct file *filp) int parport_close(struct inode *inode, struct file *filp) ssize_t parport_write(struct file *filp, char *buf, size_t count, loff_t *f_pos) Inicialização módulo Finalização módulo Abertura ficheiro Fecho ficheiro Escrita Bytes • Inicialização da impressora e comandos PCL de configuração inseridos na função de abertura do ficheiro. • Exemplifica-se o controlador de impressora em 2 modos: – E/S programada – Interrupções Programação de Sistemas Centronics : 13/33 Interface modo E/S programada (1) #include #include #include #include #include #include #include <linux/module.h> <linux/kernel.h> <linux/fs.h> <linux/ioport.h> <linux/errno.h> <asm/io.h> <asm/delay.h> /* struct file_operations,register_chrdev */ /* request_region */ /* error codes */ /* inb,outb */ /* udelay */ #define BASE 0x378 #define NPORTS 8 #define MDN 240 MODULE_LICENSE("GPL"); int parport_open(struct inode *, struct file *); int parport_release(struct inode *, struct file *); ssize_t parport_write(struct file *, char *, size_t, loff_t *); Programação de Sistemas Centronics : 14/33 Interface modo E/S programada (2) struct file_operations parport_ops={ .owner = THIS_MODULE, .write = parport_write, .open = parport_open, .release = parport_release }; #define #define #define #define PARPORT_CONTROL_STROBE PARPORT_CONTROL_AUTOFD PARPORT_CONTROL_INIT PARPORT_CONTROL_SELECT 0x1 0x2 0x4 0x8 #define #define #define #define #define PARPORT_STATUS_ERROR PARPORT_STATUS_SELECT PARPORT_STATUS_PAPEROUT PARPORT_STATUS_ACK PARPORT_STATUS_BUSY 0x8 0x10 0x20 0x40 0x80 Programação de Sistemas Centronics : 15/33 Interface modo E/S programada (3) char uel[9] = {'\x1b','%','-','1','2','3','4','5','X'}; char printerReset[2] = {'\x1b','E'}; /* mantem-se a calha de alimentacao */ char paperSource[5] = {'\x1b','&','l','0','H'}; /* tamanho papel, A4=26 */ char paperSize[6] = {'\x1b','&','l','2','6','A'}; /* margem de topo 12 linhas */ char printerTop[6] = {'\x1b','&','l','1','2','E'}; /* terminacao linha 2-CR=CR, LF=CR+LF, FF=FF */ char lineTermination[5] = {'\x1b','&','k','2','G'}; /* selecciona simbolos de conjunto PC-8, espaçamento proporcional, altura 14 pt, estilo direito, espessura negrito, fonte Times */ char fontSet[22] = {'\x1b','(','1','0','U','\x1b','(','s','1','p','1','4','v','0', 's','3','b','4','1','0','1','T'}; char dev_name[] = "Polling parport"; Programação de Sistemas Centronics : 16/33 Interface modo E/S programada (4) int init_module(void) { int result = register_chrdev(MDN,dev_name,&parport_ops); if (result<0) { printk(KERN_WARNING "can't get major %d\n",MDN); return result; } printk(KERN_INFO "Parport_ops installed!\n"); return 0; } void cleanup_module(void) { unregister_chrdev(MDN,dev_name); printk(KERN_INFO "Parport_ops removed!\n"); } int parport_release(struct inode *inode, struct file *filp){ /* close device */ return 0; } Programação de Sistemas Centronics : 17/33 Interface modo E/S programada (5) int parport_open(struct inode *inode, struct file *filp) { /* open device */ outb(~(PARPORT_CONTROL_INIT | PARPORT_CONTROL_SELECT | PARPORT_CONTROL_AUTOFD), BASE+2); udelay(5); outb(0xff,BASE+2); /* PCL commands */ parport_write( NULL,&printerReset[0],2,NULL ); parport_write( NULL,&paperSource[0],5,NULL ); parport_write( NULL,&paperSize[0],6,NULL ); parport_write( NULL,&printerTop[0],6,NULL ); parport_write( NULL,&lineTermination[0],5,NULL) ; parport_write( NULL,&fontSet[0],22,NULL ); return 0; /* success */ } Programação de Sistemas Centronics : 18/33 Interface modo E/S programada (6) ssize_t parport_write(struct file *filp, char *buf, size_t count, loff_t *f_pos) { /* Writes buf to the Data port */ char *plocal=buf; ssize_t wrote=0; unsigned char status, data; while(wrote<count) { for (;;) { /* NOTA: espera activa que a impressora esteja disponível */ status = inb(BASE+1); status = status & (PARPORT_STATUS_BUSY | PARPORT_STATUS_ERROR); if ( status == PARPORT_STATUS_ERROR ) break; udelay(50); } /* Set the data lines */ data = *plocal++; outb(data,BASE); udelay(1); /* Delay for a bit */ /* Pulse strobe */ outb(PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, BASE+2); udelay(5); /* End the pulse */ outb(PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_STROBE | PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, BASE+2); wrote++; } return wrote; } Programação de Sistemas Centronics : 19/33 Interface modo interrupção (1) #include #include #include #include #include #include #include #include <linux/module.h> <linux/kernel.h> <linux/fs.h> <asm/io.h> <asm/delay.h> <linux/sched.h> <linux/interrupt.h> <asm/semaphore.h> /* /* /* /* struct file_operations,register_chrdev */ inb,outb */ udelay */ request_irq */ #define BASE 0x378 #define IRQ 7 /* NOTA: novo! */ #define MDN 240 MODULE_LICENSE("GPL"); int parport_open(struct inode *, struct file *); int parport_release(struct inode *, struct file *); ssize_t parport_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos); Programação de Sistemas Centronics : 20/33 Interface modo interrupção (2) struct file_operations parport_ops={ .owner = THIS_MODULE, .write = parport_write, .open = parport_open, .release = parport_release }; #define #define #define #define #define PARPORT_CONTROL_STROBE PARPORT_CONTROL_AUTOFD PARPORT_CONTROL_INIT PARPORT_CONTROL_SELECT PARPORT_CONTROL_EN_IRQ 0x1 0x2 0x4 0x8 0x10 /* NOTA: novo! */ #define #define #define #define #define PARPORT_STATUS_ERROR PARPORT_STATUS_SELECT PARPORT_STATUS_PAPEROUT PARPORT_STATUS_ACK PARPORT_STATUS_BUSY 0x8 0x10 0x20 0x40 0x80 Programação de Sistemas Centronics : 21/33 Interface modo interrupção (3) char dev_name[] = "Interrupt parport"; struct semaphore byte_rec; /* semáforo para espera de IRQ */ irqreturn_t IRQ_handler(int irq, void *dev_id); int total_esc,num_int; /* Contador de bytes enviados e total de interrupções */ /* Constantes PCL */ char uel[9] = { '\x1b','%','-','1','2','3','4','5','X' }; char printerReset[2] = { '\x1b','E' }; /* Mantem-se a calha de alimentação */ char paperSource[5] = { '\x1b','&','l','0','H' }; /* Tamanho papel, A4=26 */ char paperSize[6] = { '\x1b','&','l','2','6','A' }; /* Margem de topo 12 linhas */ char printerTop[6] = { '\x1b','&','l','1','2','E' }; /* terminacao linha 2-CR=CR, LF=CR+LF, FF=FF */ char lineTermination[5] = { '\x1b','&','k','2','G' }; /* selecciona símbolos de conjunto PC-8, espaçamento proporcional, altura 14 pt, estilo direito, espessura negrito, fonte Times */ char fontSet[22] = { '\x1b','(','1','0','U','\x1b','(','s','1','p','1', '4','v','0','s','3','b','4','1','0','1','T' }; Programação de Sistemas Centronics : 22/33 Interface modo interrupção (4) irqreturn_t IRQ_handler(int irq, void *dev_id) { /* NOTA: nova função, desbloqueia gestor de semáforo */ up(&byte_rec); num_int++; return IRQ_HANDLED; } int init_module(void) { int result = register_chrdev(MDN,dev_name,&parport_ops); if (result<0) { printk(KERN_WARNING "can't get major %d\n",MDN); return result; } printk(KERN_INFO "Parport_int installed!\n"); return 0; } void cleanup_module(void) { unregister_chrdev(MDN,dev_name); printk(KERN_INFO "Parport_int removed!\n"); } Programação de Sistemas Centronics : 23/33 Interface modo interrupção (5) int parport_release(struct inode *inode,struct file *filp) { /* NOTA: libertar IRQ que já não é necessario */ free_irq(IRQ, NULL); /* desactiva geração de interupção da porta paralela */ outb(PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_STROBE | PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, BASE+2); printk(KERN_INFO "Parport_int closed\n Written %d\n interrupts %d\n", total_esc, num_int); return 0; } Programação de Sistemas Centronics : 24/33 Interface modo interrupção (6) int parport_open(struct inode *inode, struct file *filp) { int result; /* open device */ printk(KERN_INFO "Parport_int open\n"); total_esc = num_in t= 0; /* NOTA: Instalar função de tratamento de IRQ */ result= request_irq(IRQ, IRQ_handler, SA_INTERRUPT, dev_name, NULL); if (result!=0) { printk(KERN_WARNING "can't get IRQ %d\n",IRQ); return result; } Programação de Sistemas Centronics : 25/33 Interface modo interrupção (7) /* inicializar impressora na porta paralela */ outb(PARPORT_CONTROL_INIT | PARPORT_CONTROL_SELECT | PARPORT_CONTROL_AUTOFD, BASE+2); udelay(5); outb( 0xff & (~PARPORT_CONTROL_EN_IRQ),BASE+2 ); /* inicializar semáforo bloqueado para esperar chegada da interrupção */ init_MUTEX_LOCKED(&byte_rec); /* comandos PCL de inicialização da impressora */ parport_write( NULL,&printerReset[0],2,NULL ); parport_write( NULL,&paperSource[0],5,NULL ); parport_write( NULL,&paperSize[0],6,NULL ); parport_write( NULL,&printerTop[0],6,NULL ); parport_write( NULL,&lineTermination[0],5,NULL ); parport_write( NULL,&fontSet[0],22,NULL ); return 0; /* success */ } Programação de Sistemas Centronics : 26/33 Interface modo interrupção (8) ssize_t parport_write(struct file *filp, char *buf, size_t count, loff_t *f_pos) { /* Writes buf to the Data port */ char *plocal=buf; ssize_t wrote=0; unsigned char status, data; /* Esperar que a impressora esteja pronta para iniciar impressão (útil caso a impressora esteja a terminar alguma impressão) */ for (;;) { status = inb(BASE+1); status = status & (PARPORT_STATUS_BUSY | PARPORT_STATUS_ERROR); if ( status == PARPORT_STATUS_ERROR ) break; udelay(50); } while(wrote<count) { /* Set the data lines */ data = *plocal++; outb(data,BASE); udelay(1); /* Delay for a bit */ Programação de Sistemas Centronics : 27/33 Interface modo interrupção (9) /* Pulse strobe */ outb(PARPORT_CONTROL_EN_IRQ | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, BASE+2); udelay(5); /* End the pulse */ outb(PARPORT_CONTROL_EN_IRQ |PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_STROBE | PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, BASE+2); /* A esperar IRQ * / if ( down_interruptible(&byte_rec)!=0 ) break; wrote++; total_esc++; } printk(KERN_INFO "Parport_int: Foram escritos %d bytes\n", wrote); return wrote; } Programação de Sistemas Centronics : 28/33 Instalação do controlador (1) 1. Gerar o controlador [root@manitoba parport]#make –C /lib/modules/`uname-r`/build=M=`pwd` modules make: Entering directory `/usr/src/kernels/2.6.18-1.2798.fc6-i686' CC [M] /root/parport/parport_ops.o /root/parport/parport_ops.c: In function ‘parport_write’: /root/parport/parport_ops.c: In function ‘init_module’: /root/parport/parport_ops.c:140: warning: assignment makes integer from pointer without a cast Building modules, stage 2. MODPOST CC /root/parport/parport_ops.mod.o LD [M] /root/parport/parport_ops.ko make: Leaving directory `/usr/src/kernels/2.6.18-1.2798.fc6-i686' Programação de Sistemas Centronics : 29/33 Instalação do controlador (2) 2. Criar um i-node [root@manitoba parport]# mknod /dev/parport_ops c 240 0 3. Inserir o módulo no sistema [root@manitoba parport]# /sbin/insmod parport_ops.ko Nota: efeito da função init_module() pode ser observado no ficheiro /var/log/messages Mar 29 17:51:54 manitoba kernel: Parport_ops installed! A instalação pode ser confirmada – Pelo comando lsmod Module Size parport_ops Used by 7296 0 – No directório /dev/ [root@manitoba parport]# ls -l /dev/parport_ops crw-rw-rw- 1 root root 240, 0 Mar 29 14:50 /dev/parport_ops Programação de Sistemas Centronics : 30/33 Instalação do controlador (3) 4. Experimentar com o programa #include <stdio.h> #define SIZE 128 main(int argc, char **argv) { int fsA; FILE *fsB; int count; char buf[SIZE]; if (argc!=2) { fprintf( stderr,"Apenas 1 parametro-ficheiro a despejar\n" ); exit(12); } fsA = open( "/dev/parport_ops",O_WRONLY ); if(fsA<0) { perror(""); exit(10); } fprintf( stderr,"Abri /dev/parport_ops\n" ); Programação de Sistemas Centronics : 31/33 Instalação do controlador (4) fsB = fopen( argv[1],"r" ); if(fsB==NULL) { perror(""); exit(10); } fprintf( stderr,"Abri %s\n",argv[1 ]); do { count = fread(&buf[0],1,SIZE,fsB); if(count>0) write(fsA,&buf[0],count); } while (count!=0); close(fsA); fclose(fsB); } Programação de Sistemas Centronics : 32/33 Instalação do controlador (5) 5. Depois de experimentar, eliminar módulo do sistema [root@manitoba rgc]# /sbin/rmmod /dev/parport_ops 6. Eliminar referência a dispositivo [root@manitoba rgc]# rm /dev/parport_ops rm: remove character special file `/dev/parport_ops'? y [root@manitoba rgc]# Programação de Sistemas Centronics : 33/33