Programação de Sistemas
Controlador de impressora
por interface paralela
Programação de Sistemas
Centronics : 1/33
• 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.
Introdução (1)
– Pinos distribuídos por:
•
8 bidireccionais para porto OUTPUT (RW)
•
5 para porto STATUS(R)
•
4 para porto CONTROL(RW)
•
8 ligados ao GND
• 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
Introdução (2)
Programação de Sistemas
Centronics : 3/33
• 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.
Status register
Bit
Pino
Sinal
Significado
3
15
Error/
Perifério em erro
4
13
Select
Perifério seleccionado
5
12
PaperOut
Sem papel
Ligado ao IRQ7
Introdução (3)
6
10
Ack/
Aceitou dados (mantém LOW durante ~ 5µs)
7
11
Busy
Perifério ocupado
Control register
Bit
Pino
Sinal
Significado
0
1
Strobe/
Dados disponíveis (manter LOW durante min 5µs)
1
14
AutoLineFeed/
Ordem para executar automaticamente salto de linha
• O controlador vai interagir com impressora em modo
E/S programada (“pooling”).
• Diagrama temporal de envio de um Byte para o
periférico.
Introdução (4)
Programação de Sistemas
Centronics : 5/33
• Uso do porto paralelo para controlar um step
motor
• 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.
Impressora (1)
Programação de Sistemas
Centronics : 7/33
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.
• Comandos da impressora expressos na linguagem
PJL-Printer Job Language da HP.
• Uma tarefa de impressão segue a seguinte estrutura
<ESC>%-12345X
Comandos controlo da tarefa
Impressora (2)
Comandos controlo da tarefa
<ESC>E
Página 1
…
Página n
<ESC>E
<ESC>%-12345X
• 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.
Impressora (3)
Programação de Sistemas
Centronics : 9/33
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
• Os comandos PCL possuem o formato
<ESC>C
parameterizado
C
grupo
#C
terminação
Opcionalmente mais dois campos,
C
parâmetro#
Impressora (4)
quantidade
• O sistema de coordenadas é
definido pela figura
• O texto é impresso apenas
dentro das margens (tudo o
que saia é eliminado)
Margens
(0,0)
X
•
Comandos PCL de controlo de página: exemplos
<ESC>&l#A
### tamanho página (para A4, #=26)
<ESC>&l#H
### fonte papel (para manual, #=2)
<ESC>&l#O
### orientação papel (para vertical, #=0)
•
Fontes determinadas por parâmetros na seguinte ordem:
Impressora (5)
Programação de Sistemas
Centronics : 11/33
1.
Conjunto de símbolos (PC-8,…)
2.
Espaçamento ocupado por cada caractere (igual ou distinto)
3.
Pitch (número de caracteres impressos numa polegada horizontal)
4.
Altura (diferença em pontos=1/72 polegadas)
5.
Estilo (postura-ex:itálico, largura e estrutura-ex:sombreado)
6.
Espessura (“stroke weight”)
7.
Tipo de letra (Courier,CG Times,…)
Hp
<ESC>(0U<ESC>(s1p14v0s3b4101T
ASCII
espaçamento proporcional
altura
CG Times
Impressora (6)
•
Comandos PCL de posicionamento
<ESC>*p#x#Y
### posiciona cursor na linha, coluna
•
Terminação de linha
• Funções a implementar:
int init_module(void)
Inicialização módulo
void cleanup_module(void)Finalização módulo
int parport_open(struct inode *inode,
Abertura ficheiro
struct file *filp)int parport_close(struct inode *inode,
Fecho ficheiro
Interface
Programação de Sistemas
Centronics : 13/33
struct file *filp)
ssize_t parport_write(struct file *filp,
Escrita Bytes
char *buf, size_t count, loff_t *f_pos)• 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
#include <linux/module.h> #include <linux/kernel.h>
#include <linux/fs.h> /* struct file_operations,register_chrdev */ #include <linux/ioport.h> /* request_region */
#include <linux/errno.h> /* error codes */ #include <asm/io.h> /* inb,outb */ #include <asm/delay.h> /* udelay */
Interface modo E/S programada (1)
#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 *);
struct file_operations parport_ops={ .owner = THIS_MODULE, .write = parport_write, .open = parport_open, .release = parport_release }; #define PARPORT_CONTROL_STROBE 0x1 #define PARPORT_CONTROL_AUTOFD 0x2
Interface modo E/S programada (2)
Programação de Sistemas
Centronics : 15/33
#define PARPORT_CONTROL_AUTOFD 0x2 #define PARPORT_CONTROL_INIT 0x4 #define PARPORT_CONTROL_SELECT 0x8 #define PARPORT_STATUS_ERROR 0x8 #define PARPORT_STATUS_SELECT 0x10 #define PARPORT_STATUS_PAPEROUT 0x20 #define PARPORT_STATUS_ACK 0x40 #define PARPORT_STATUS_BUSY 0x80 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'};
Interface modo E/S programada (3)
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'};
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; }
Interface modo E/S programada (4)
Programação de Sistemas
Centronics : 17/33
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; }
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);
Interface modo E/S programada (5)
/* 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 */
}
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);
Interface modo E/S programada (6)
Programação de Sistemas
Centronics : 19/33
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; }
#include <linux/module.h> #include <linux/kernel.h>
#include <linux/fs.h> /* struct file_operations,register_chrdev */ #include <asm/io.h> /* inb,outb */
#include <asm/delay.h> /* udelay */ #include <linux/sched.h> /* request_irq */ #include <linux/interrupt.h>
Interface modo interrupção (1)
#include <asm/semaphore.h> #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 *);
struct file_operations parport_ops={ .owner = THIS_MODULE, .write = parport_write, .open = parport_open, .release = parport_release }; #define PARPORT_CONTROL_STROBE 0x1
Interface modo interrupção (2)
Programação de Sistemas
Centronics : 21/33
#define PARPORT_CONTROL_STROBE 0x1 #define PARPORT_CONTROL_AUTOFD 0x2 #define PARPORT_CONTROL_INIT 0x4 #define PARPORT_CONTROL_SELECT 0x8
#define PARPORT_CONTROL_EN_IRQ 0x10 /* NOTA: novo! */
#define PARPORT_STATUS_ERROR 0x8 #define PARPORT_STATUS_SELECT 0x10 #define PARPORT_STATUS_PAPEROUT 0x20 #define PARPORT_STATUS_ACK 0x40 #define PARPORT_STATUS_BUSY 0x80
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' };
Interface modo interrupção (3)
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 */
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);
Interface modo interrupção (4)
Programação de Sistemas
Centronics : 23/33
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"); }
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 |
Interface modo interrupção (5)
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; }
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;
Interface modo interrupção (6)
Programação de Sistemas
Centronics : 25/33
/* 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; }
/* 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);
Interface modo interrupção (7)
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 );
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) */
Interface modo interrupção (8)
Programação de Sistemas
Centronics : 27/33
(ú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 */
/* 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 |
Interface modo interrupção (9)
PARPORT_CONTROL_STROBE |
PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, BASE+2); /* A esperar IRQ * /
if ( down_interruptible(&byte_rec)!=0 ) break; wrote++;
total_esc++; }
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’:
Instalação do controlador (1)
Programação de Sistemas
Centronics : 29/33
/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'
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
Instalação do controlador (2)
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
Used by
parport_ops 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
4. Experimentar com o programa
#include <stdio.h> #define SIZE 128
main(int argc, char **argv) { int fsA;
FILE *fsB; int count;
Instalação do controlador (3)
Programação de Sistemas
Centronics : 31/33
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" );
fsB = fopen( argv[1],"r" ); if(fsB==NULL) {
perror(""); exit(10); }
fprintf( stderr,"Abri %s\n",argv[1 ]);
do {