desenvolvimento de um modem plc em plataforma linux

100
UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL ESCOLA DE ENGENHARIA DEPARTAMENTO DE ENGENHARIA EL ´ ETRICA CURSO DE GRADUA ¸ C ˜ AO EM ENGENHARIA EL ´ ETRICA DIEGO CABERLON SANTINI DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX Porto Alegre 2006

Upload: others

Post on 24-Jul-2022

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

UNIVERSIDADE FEDERAL DO RIO GRANDE DO SULESCOLA DE ENGENHARIA

DEPARTAMENTO DE ENGENHARIA ELETRICACURSO DE GRADUACAO EM ENGENHARIA ELETRICA

DIEGO CABERLON SANTINI

DESENVOLVIMENTO DE UMMODEM PLC EM PLATAFORMA

LINUX

Porto Alegre2006

Page 2: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX
Page 3: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

DIEGO CABERLON SANTINI

DESENVOLVIMENTO DE UMMODEM PLC EM PLATAFORMA

LINUX

Projeto de Diplomacao apresentado ao Departa-mento de Engenharia Eletrica da UniversidadeFederal do Rio Grande do Sul como parte dosrequisitos para a obtencao do tıtulo de Enge-nheiro Eletricista.

ORIENTADOR: Prof. Dr. Walter Fetter Lages

Porto Alegre2006

Page 4: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX
Page 5: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

DIEGO CABERLON SANTINI

DESENVOLVIMENTO DE UMMODEM PLC EM PLATAFORMA

LINUX

Este Projeto foi julgado adequado para a obten-cao dos creditos da Disciplina Projeto de Diplo-macao do Departamento de Engenharia Eletricae aprovado em sua forma final pelo Orientadore pela Banca Examinadora.

Orientador:Prof. Dr. Walter Fetter Lages, UFRGSDoutor pela Instituto Tecnologico de Aeronautica – SaoJose dos Campos, Brasil

Banca Examinadora:

Prof. Dr. Walter Fetter Lages, UFRGSDoutor pelo Instituto Tecnologico de Aeronautica – Sao Jose dos Campos,Brasil

Prof. Dr. Renato Ventura Bayan Henriques, UFRGSDoutor pela Universidade Federal de Minas Gerais – Belo Horizonte, Brasil

Prof. Dr. Romeu Reginatto, UFRGSDoutor pela Universidade Federal de Santa Catarina – Florianopolis, Brasil

Chefe do DELET:Prof. Dr. Roberto Petry Homrich

Porto Alegre, dezembro de 2006.

Page 6: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX
Page 7: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

DEDICATORIA

Dedico esse trabalho para todas as pessoas que diretamente ou indiretamentecontribuıram para o meu crescimento intelectual ao longo da minha vida academica.

Page 8: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX
Page 9: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

AGRADECIMENTOS

Primeiramente gostaria de agradecer aos meus pais, Gilberto Santini e Maria InesCaberlon Santini, por toda a estrutura que me tem oferecido ao longo da minha vidae especialmente pelo o esforco que fizeram para que eu me tornasse um EngenheiroEletricista.

Tambem agradeco ao meu irmao, Thiago Caberlon Santini, pelo incentivo quemeu deu ao longo da minha vida.

Ao meu orientador, Walter Fetter Lages, pelos tres anos sobre a sua orientacaopelo qual muito devo o meu atual conhecimento.

Ao meu supervisor, Julio Klemm, pela a oportunidade me dada para consolidarmeus conhecimentos durante o estagio.

A Universidade Federal do Rio Grande do Sul e aos seus funcionarios pela cons-tante infra-estrutura oferecida, e pela a oportunidade de estudar gratuitamente emuma das melhores universidades do Brasil.

Por fim, aos meus colegas que me apoiaram durante a faculdade e que, comcerteza, nao chegaria ate aqui sem eles.

Page 10: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX
Page 11: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

RESUMO

Este trabalho trata dos passos do desenvolvimento de um modem PLC que estasendo construindo no LASCAR, utilizando o chip ST7538. Neste trabalho constauma breve descricao a respeito da comunicacao PLC. A seguir serao apresentadas ascaracterısticas do hardware que foi desenvolvido, incluindo a parte digital e analogicado modem. Finalmente sera explorado a parte de software do mesmo, com umaintroducao a sistemas operacionais e o linux, com o desenvolvimento de driver TTYpara plataforma Linux-2.6.17 com os protocolos HDLC, KISS, TCP/UDP, IP.

Palavras-chave: PLC, modem PLC, Linux, drivers TTY, ST7538.

Page 12: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX
Page 13: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

ABSTRACT

This work deals with the steps for chip development of a PLC modem that isbeing built at LASCAR, with ST7538. This work present a description of PLC com-munication, its advantages and disadvantages. In the sequence, the characteristicsof the hardware are described, including digital and analogical components. Finallythe software will be explored, with an introduction to operational system and Linuxand with the development of a TTY driver for Linux-2.6.17 with the HDLC, KISS,TCP/UDP and IP protocols.

Keywords: PLC, modem PLC, Linux, drivers TTY, ST7538.

Page 14: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX
Page 15: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

SUMARIO

LISTA DE ILUSTRACOES . . . . . . . . . . . . . . . . . . . . . . . . . . 15

LISTA DE TABELAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

LISTA DE ABREVIATURAS . . . . . . . . . . . . . . . . . . . . . . . . . 19

1 INTRODUCAO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211.1 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211.2 Organizacao do trabalho . . . . . . . . . . . . . . . . . . . . . . . . 22

2 TECNOLOGIA PLC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232.1 Introducao a Tecnologia PLC . . . . . . . . . . . . . . . . . . . . . 232.1.1 X-10 Uma Primeira Ideia . . . . . . . . . . . . . . . . . . . . . . . . . 232.1.2 Problemas da Tecnologia PLC . . . . . . . . . . . . . . . . . . . . . . 242.1.3 Vantagens da Tecnologia PLC . . . . . . . . . . . . . . . . . . . . . . 242.2 Tipos de Redes PLC . . . . . . . . . . . . . . . . . . . . . . . . . . . 262.2.1 Last Mile Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262.2.2 Last Inch Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

3 HARDWARE DO MODEM PLC . . . . . . . . . . . . . . . . . . . . . 293.1 Parte Analogica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293.1.1 ST7538-PowerLine FSK Transceiver . . . . . . . . . . . . . . . . . . 293.1.2 Componente Analogicos . . . . . . . . . . . . . . . . . . . . . . . . . 323.2 Parte Digital . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323.2.1 Interface com a Porta Paralela . . . . . . . . . . . . . . . . . . . . . . 323.3 Mudanca de Hardware . . . . . . . . . . . . . . . . . . . . . . . . . 35

4 SOFTWARE DO MODEM PLC . . . . . . . . . . . . . . . . . . . . . 374.1 Plataforma de Desenvolvimento do Software . . . . . . . . . . . . 374.1.1 Sistemas Operacionais . . . . . . . . . . . . . . . . . . . . . . . . . . 384.2 Linguagem C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404.2.1 Programando no Kernel do Linux . . . . . . . . . . . . . . . . . . . . 404.3 Especificacoes de Hardware . . . . . . . . . . . . . . . . . . . . . . 424.4 Kernel Space: Driver TTY . . . . . . . . . . . . . . . . . . . . . . 424.4.1 Funcoes de Acesso ao Hardware . . . . . . . . . . . . . . . . . . . . . 434.4.2 Funcoes de Protocolos . . . . . . . . . . . . . . . . . . . . . . . . . . 444.4.3 Funcoes de Driver TTY . . . . . . . . . . . . . . . . . . . . . . . . . 494.5 User Space: kissattach . . . . . . . . . . . . . . . . . . . . . . . . . 50

Page 16: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

5 CONCLUSAO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

REFERENCIAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

APENDICE A DIAGRAMAS ESQUEMATICOS DO MODEM PLC . . . 59

APENDICE B ARTE FINAL DO PCB DO MODEM PLC . . . . . . . . 63

APENDICE C CODIGO FONTE DO DRIVER DO MODEM PLC . . . 69

APENDICE D HEADER DA PLACA, LPMODEM.H . . . . . . . . . . . 95

APENDICE E HEADER DO ST7538, ST7538.H . . . . . . . . . . . . . 97

Page 17: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

LISTA DE ILUSTRACOES

Figura 2.1: Modem X-10. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24Figura 2.2: Rede Ethernet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25Figura 2.3: Rede usando a linha telefonica. . . . . . . . . . . . . . . . . . . . 25Figura 2.4: Rede usando a rede de energia eletrica. . . . . . . . . . . . . . . . 26Figura 2.5: Last inch access, ou acesso dentro das residencias. . . . . . . . . . 27

Figura 3.1: Diagrama de blocos do ST7538. . . . . . . . . . . . . . . . . . . . 30Figura 3.2: Modulacao FSK. . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

Figura 4.1: Divisao do kernel do Linux. . . . . . . . . . . . . . . . . . . . . . 39Figura 4.2: Adicionando um Driver no Kernel. . . . . . . . . . . . . . . . . . 41Figura 4.3: Fluxograma da leitura e escrita de dados no ST7538. . . . . . . . 43Figura 4.4: Sequencia de escrita no registrador interno do ST7538. . . . . . . 44Figura 4.5: Fluxograma do software de leitura e escrita de dados no ST7538. 45Figura 4.6: Leitura do registrador interno do ST7538 realizada pelo driver. . . 45Figura 4.7: Fluxograma do software de leitura e escrita de dados no ST7538. 46Figura 4.8: Envio de dados para o ST7538. . . . . . . . . . . . . . . . . . . . 46Figura 4.9: Estrutura do Frame HDLC. . . . . . . . . . . . . . . . . . . . . . 47Figura 4.10: Envio da sequencia de flags para o ST7538. . . . . . . . . . . . . 47Figura 4.11: Envio da sequencia de flags para o ST7538 (zoom). . . . . . . . . 48Figura 4.12: Configuracao do Arquivo /etc/ax25/axports. . . . . . . . . . . . . 51Figura 4.13: Utilizando o kissattach. . . . . . . . . . . . . . . . . . . . . . . . 51Figura 4.14: Display do ifconfig. . . . . . . . . . . . . . . . . . . . . . . . . . . 52Figura 4.15: Modem Funcionando. . . . . . . . . . . . . . . . . . . . . . . . . . 53Figura 4.16: Transmissao de dados para o modem PLC. . . . . . . . . . . . . . 53Figura 4.17: Recepcao de dados pelo o modem PLC. . . . . . . . . . . . . . . 54

Figura 5.1: Foto do Modem PLC. . . . . . . . . . . . . . . . . . . . . . . . . 55

Page 18: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX
Page 19: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

LISTA DE TABELAS

Tabela 3.1: Ligacao do registrador de controle. . . . . . . . . . . . . . . . . . 33Tabela 3.2: Ligacao dos pinos de entrada. . . . . . . . . . . . . . . . . . . . . 33Tabela 3.3: Ligacao dos pinos de saıda. . . . . . . . . . . . . . . . . . . . . . 34Tabela 3.4: Ligacao nos multiplexadores. . . . . . . . . . . . . . . . . . . . . 35Tabela 3.5: Ligacao dos pinos de saıda. . . . . . . . . . . . . . . . . . . . . . 35

Tabela 4.1: Tipo de frame do protocolo KISS. . . . . . . . . . . . . . . . . . . 49

Page 20: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX
Page 21: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

LISTA DE ABREVIATURAS

ASK Amplitude-Shift Keying

CRC Cyclic Redundancy Check

CSMA Carrier Sense Multiple Access

EXT2 Second Extended File System

EXT3 Third Extended File System

FAT File Allocation Table

FEND Frame End

FESC Frame Escape

FR Frame Relay

FSK Frequency-shift keying

GPL General Public License

HD Hard Data

HDLC High Level Data Link Control

IA32 Intel Architecture, 32-bits

IP Internet Protocol

JFFS2 The Journalling Flash File System, version 2

KISS Keep it Simple Stupid

LASCAR Laboratorio de Sistemas de Controle, Automacao e Robotica

NTFS New Technology File System

OSI Open Systems Interconection

PCB Printed Circuit Board

PLC PowerLine Communication

PLIP Parallel Line Internet Protocol

PPP Point-to-Point Protocol

PSK Phase-Shift Keying

SLIP Serial Line Internet Protocol

Page 22: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

TCP Transmission Control Protocol

TEND Transposed Frame End

TFESC Transposed Frame Escape

TTY Teletype

xDSL Digital Subscriber Line

Page 23: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

21

1 INTRODUCAO

A tecnologia PowerLine Communication PLC permite transmitir dados e infor-macoes utilizando os cabos da rede eletrica como meio fısico. Possuindo assim comoprincipal vantagem sobre as outras tecnologias de transmissao o fato da utilizacaode uma infra-estrutura fısica ja existente, pois em praticamente todos os lugaresha uma rede eletrica instalada. Isso a torna uma solucao pratica para as seguintesnecessidades:

1. automacao residencial, pois, como por necessidade, quase todo o equipamentoja esta ligado a rede eletrica.

2. telemetria.

3. internet banda larga.

Visando tais aplicacoes, especialmente a telemetria, foi implementada no LAS-CAR uma placa de desenvolvimento de um modem PLC baseada no chip ST7538(Powerline FSK Transceiver) da STMicroelectronics.

Tal aparelho foi desenvolvido para se conectar a rede de energia eletrica, utili-zando modulacao FSK com uma portadora 132.5kHz. Ele possui uma alimentacaoindependente de 10V e a troca de dados com um computador IBM-PC ocorre atravesda interface da porta paralela. Tais passos serao desenvolvidos no Capıtulo 3.

Para utilizar o modem PLC torna-se necessario o desenvolvimento de um soft-ware que controle os sinais do chip. Utilizando um computador rodando Linux,com o kernel 2.6.17, sera implementado um driver para a troca de informacoes dokernel com a aplicacao. Tal driver devera ser responsavel por fazer com que quedois modems nao transmitam ao mesmo tempo na rede eletrica. Para isso sera im-plementado um controle de acesso ao meio CSMA-ppersitente. Para a camada deenlace, o driver encapsulara os dados em HDLC e utilizara o tipo de transmissaobroadband, onde todos os modems recebem o que esta sendo transmitido.

1.1 Objetivos

O objetivo deste trabalho e explorar as caracterısticas do chip ST7538, utilizando-o no desenvolvimento de um modem PLC, implementando o hardware e o softwareadequados para a tarefa em questao. Os esquematicos de hardware e a arte finalPCB se encontram em anexo no final desse trabalho e podem ser encontrados em<http://www.ece.ufrgs.br/~fetter>. O software do modem tera uma licencaGPL, o qual permite ser copiado, distribuıdo e melhorado, sendo desenvolvido para

Page 24: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

22

ser utilizado com o kernel 2.6.17, possibilitando que pessoas que necessitem utilizartal solucao possam usufruir de um modem PLC completo para as suas finalidades.

A principal motivacao deste trabalho e finalizar o desenvolvimento do modemPLC com licenca GPL, demonstrando todas as qualidades adquiridas ao longo docurso para tal, para que todas as pessoas possam utilizar tal tecnologia como umaopcao viavel para a transmissao de dados a baixas velocidades em redes residenciaisinternas.

1.2 Organizacao do trabalho

Este trabalho esta organizado em outros quatro capıtulos. No Capıtulo 2 saoapresentados as caracterısticas da comunicacao PLC. O Capıtulo 3 apresenta ascaracterısticas do chip ST7538 e dos componentes presentes na placa. No Capıtuloseguinte, desenvolve-se nocoes de Linux, funcoes para acessar ao hardware da placae dos protocolos que serao suportados pelo o modem e a utilizacao do softwarekissattach e a sua com utilidade.

Page 25: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

23

2 TECNOLOGIA PLC

Este capıtulo tem por objetivo apresentar aspectos basicos da tecnologia PLC.

2.1 Introducao a Tecnologia PLC

A tecnologia PLC usa como meio fısico uma das redes mais utilizadas em todo omundo: a rede de energia eletrica. A ideia desta tecnologia nao e nova e isto podeser comprovado se lembrarmos do antigo sistema ”Baba Eletronica”que mesmo tendomais de 30 anos, e utilizado ate hoje (IGUACU ENERGIA, 2006). Entretanto talmeio possui uma grande quantidade de equipamentos conectados a ele, que interferena comunicacao PLC. Devido a esses problemas, somente ha aproximadamente qua-tro anos, com a evolucao da tecnologia, a comunicacao PLC comecou a ser avaliadae utilizada por algumas empresas. Assim, aproveitando o fenomeno da propagacaode ondas atraves dos condutores eletricos e desenvolvendo uma transmissao de formacontrolada (frequencias e nıveis de propagacao especıficos) com sinais que utilizamas frequencias de forma inteligente e diferenciada daquelas utilizadas por outrosequipamentos e pela propria transmissao da energia eletrica, nasceram os primeirosmodems PLC.

2.1.1 X-10 Uma Primeira Ideia

Uma das primeiras formas de implementar a comunicacao PLC foi atraves datecnologia X-10. Ela foi desenvolvida para ser unidirecional, porem atualmente epossıvel encontrar dispositivos bidirecionais. Os controladores X-10 enviam seussinais para receptores atraves da rede de energia eletrica e sao utilizado basicamentepara controlar lampadas e outras aplicacao simples. A Figura 2.1 apresenta a fotode um modem comercial PLC utilizando a tecnologia X-10.

A tecnologia X-10 utiliza uma forma de modulacao Amplitude-Shift Keying (ASK),onde uma portadora modulada de 120kHz com um sinal de 0.5W de energia, e trans-mitida a cada cruzamento por zero da rede de energia eletrica. Para aumentar arobustez do sistema, cada bit utiliza dois cruzamentos para ser transmitido. A trans-missao no cruzamento por zero e justificada, pois neste instante apresenta um menornıvel de ruıdo na rede (NETWORKING, 2006).

Essa forma de representacao do sinal apresenta um pequena largura de banda(60 bps) e fica severamente comprometida se o ambiente de transmissao for muitoruidoso, por esses motivos tal tecnologia ficou limitada a aplicacoes simples.

Page 26: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

24

Figura 2.1: Modem X-10.

2.1.2 Problemas da Tecnologia PLC

A tecnologia PLC seria uma solucao perfeita se nao fosse pelo fato de as linhas deforca (assim como a rede telefonica no passado) nao serem consideradas meios ideaispara a transmissao de dados. Tanto dentro e como fora de casa, a rede eletrica estasujeita a todo tipo de interferencia e ruıdos gerados pela diversidade de equipamentosque estao conectados a ela, tais como fontes chaveadas, motores e dimmers. Essesequipamentos geram durante o seu funcionamento uma serie de harmonicas da redeeletrica que pode inviabilizar a comunicacao PLC.

Outro fator negativo das redes eletricas e sua oscilacao: caracterısticas comoimpedancia, atenuacao podem variar drasticamente de um momento para o outro,a medida que luzes ou aparelhos conectados a rede sao ligados ou desligados. Alemdisso, se a intencao for transmitir informacoes a longas distancias, os transformadoresde distribuicao sao verdadeiras barreiras para a transferencia de dados. Apesar depermitirem a passagem de corrente alternada a 50 Hz ou 60 Hz com quase 100% deeficiencia, os transformadores atenuam seriamente outros sinais de maior frequencia,tal fato foi de fundamental importancia para a realizacao de um modem para a redeinterna.

2.1.3 Vantagens da Tecnologia PLC

A principal vantagem da tecnologia PLC e a possibilidade de transformar todaa infra-estrutura eletrica de uma residencia ou edifıcio em uma rede local de dados,baseado no conceito de ”aproveitamento da rede eletrica”. A conveniencia e atemesmo mais obvia neste caso porque enquanto nem todo comodo tiver um telefonepara conectar, sempre existira uma tomada perto de um computador e cada tomadapode ser encarada como um ponto de acesso que pode ser usado de maneira simples edescomplicada. Logo, por nao requerer nenhuma instalacao nova, a tecnologia PLCe um bom metodo de conectar computadores em comodos diferentes. Vale ressaltarque uma rede domestica PLC nao acrescenta custo a conta de energia eletrica. AsFiguras 2.2, 2.3, 2.4 apresentam os exemplos de uma rede de dados para umacasa residencial. A Figura 2.2 mostra uma rede Ethernet onde os poucos pontosde acessos estao localizados somente pertos de computadores. A Figura 2.3 mostrauma rede usando a linha telefonica onde existem mais pontos de acessos localizadosescassamente por toda a casa. A Figura 2.4 mostra uma rede usando a linha deenergia onde existem muitos pontos de acessos localizados por todos os comodos da

Page 27: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

25

casa.

Figura 2.2: Rede Ethernet.

Figura 2.3: Rede usando a linha telefonica.

Essa ideia de transmitir dados sobre rede eletrica tambem poderia ser aplicadapara interconectar dispositivos inteligentes dentro de uma casa. No inıcio de 2000, aempresa Sunbeam anunciou uma linha de eletrodomesticos inteligentes que trocavaminformacoes no momento em que eram ligados a tomada. Batizada de HLT (HomeLinking Technology), a iniciativa pretendia lancar produtos como despertadores,detectores de fumaca, cafeteiras, cobertores eletricos, medidores de pressao arterial,capazes de se comunicar. Por exemplo, o despertador poderia ser programado paramandar uma ordem a cafeteira para comecar a preparar o cafe um pouco antes

Page 28: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

26

Figura 2.4: Rede usando a rede de energia eletrica.

do pessoal da casa sair da cama. Num futuro proximo, ate sera possıvel colocarum filme em DVD no PC da casa e transmitir o som e a imagem para uma TVcompatıvel pela fiacao interna (REDES, 2006).

2.2 Tipos de Redes PLC

O mercado atual envolvendo a tecnologia PLC encontra-se dividido em dois seg-mentos: last mile access e last inch access

2.2.1 Last Mile Access

Last mile access: seria o acesso ate a residencias, onde o PLC e somente uma dasdiversas possibilidades tecnologicas dentre as quais figura o cable modem e diferentestipos de xDSL e difusao sem fio (broadband wireless). Nenhuma das tecnologias sedestaca com relacao as demais; nem o PLC apresenta grande vantagens tecnicas comrelacao as outras tecnicas, nem estas sao muito superiores ao PLC (MAJUMDER;CAFFREY, 2004). E nesse segmento do mercado que as grandes concessionariasestao interessadas, pois a tecnologia PLC usaria as linhas de transmissao das mesmasabrindo um novo setor de mercado para elas.

2.2.2 Last Inch Access

Last inch access: seria o acesso dentro das residencias. Alguns estudos apontamque o uso de PLC seria mais eficaz que o cabo, ou mesmo o wireless para prover oacesso no interior das residencias (MAJUMDER; CAFFREY, 2004), devido a capi-laridade da sua rede. O last inch access esta conduzindo gradativamente a expansaodo espectro das redes caseiras, por meio de um vasto conjunto de equipamentosconectados no interior das habitacoes por uma rede interna (in-home). Tal redepode transformar, como dito anteriormente, todas as tomadas eletricas da casa emconexoes de difusao para computadores pessoais, telefones, e seus acessorios, bemcomo para outro dispositivos eletro-eletronicos, como ilustrado na Figura 2.5.

Page 29: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

27

Figura 2.5: Last inch access, ou acesso dentro das residencias.

Page 30: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

28

Page 31: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

29

3 HARDWARE DO MODEM PLC

Neste capıtulo serao expostas as caracterısticas do hardware do modem PLC.O hardware do modem e dividido em basicamente duas partes:uma parte analogicaconstituıda do chip ST7538 e uma interface dos sinais digitais para a porta paralelade um computador IBM-PC padrao.

3.1 Parte Analogica

A parte analogica do modem e constituıda de um chip ST7538 (PowerLine FSKTransceiver) e dos componentes necessarios para o seu funcionamento.

3.1.1 ST7538-PowerLine FSK Transceiver

O chip ST7538 e um transceiver que foi projetado para realizar comunicacoes se-riais half-duplex, como descrito em 3.1.1.1, utilizando a modulacao FSK, como 3.1.1.2sobre as linhas de energia eletrica. Esse chip apresenta uma serie de pinos de saıdapara indicar o seu status sob diversas condicoes, e quatro pinos de entrada pararealizar o controle sobre o modem. Internamente apresenta um registrador de 24bits onde e possıvel programar diversas opcoes a cerca do seu funcionamento. Pos-sui uma alimentacao analogica de 7.5V a 12.5V e uma alimentacao digital de 5V, enecessita de um clock de 16 MHz para o seu correto funcionamento. A Figura 3.1apresenta o diagrama de blocos do ST7538.

3.1.1.1 Comunicacao Half-Duplex

Uma comunicacao e dita half-duplex (tambem chamada semi-duplex) quandoexistem um dispositivo transmissor e outro receptor, sendo que ambos podem trans-mitir e receber dados, porem nao simultaneamente, a transmissao tem sentido bi-direcional. Durante uma transmissao half-duplex, em determinado instante um dis-positivo A sera transmissor e o outro B sera receptor, em outro instante os papeispodem se inverter. Esse caso se aplica perfeitamente para as redes de energia ele-trica, pois existe apenas um meio de transmissao e o modem envia e recebe na mesmafrequencia, logo dois modems nao podem transmitir ao mesmo tempo.

Caso existisse apenas um modem receptor e um transmissor, tal comunicacaoseria simplex, ou ainda no caso de que fosse possıvel que o modem recebesse etransmitisse simultaneamente, tal comunicacao seria full-duplex.

Page 32: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

30

Figura 3.1: Diagrama de blocos do ST7538.

3.1.1.2 Modulacao FSK

A modulacao FSK tambem conhecida por modulacao de chaveamento de frequen-cia e um tecnica de modulacao digital que consiste na mudanca da frequencia daonda portadora de acordo com o sinal digital modulante. O modem PLC possui umaonda portadora de alta frequencia, que ira variar +δ para representar uma marca e−δ para representar um espaco.

A Figura 3.2 mostra um sinal digital a ser transmitido atraves dessa modulacaoe o seu equivalente apos a modulacao.

Figura 3.2: Modulacao FSK.

Ainda e possıvel citar que existem a modulacao ASK, onde e variada a amplitudeda onda portadora, como no caso do X10 2.1.1 e a modulacao PSK, onde ocorre uma

Page 33: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

31

mudanca na fase da portadora, esta modulacao e utilizada em modems telefonicosde pequena banda.

3.1.1.3 Registrador Interno

Como mencionado anteriormente, o ST7538 possui um registrador interno quepossibilita programar algumas opcoes de funcionamento do mesmo. A leitura e es-crita desse registrador e sempre feita de forma sıncrona utilizando os pinos REG/DATA,RX/TX, RxD, TxD, CLRT.

Para realizar uma leitura nesse registrador e necessario colocar o pino REG/DATAe RX/TX em alto, a seguir deve-se ler o pino RxD a cada borda crescente do clockdurante os proximos 24 ciclos, a parte mais significativa do registrador e enviadaantes. Para realizar um escrita o procedimento e similar, porem deve-se deixar oRX/TX em baixo e escrever os dados no TxD. Somente os ultimos 24 bits seraoescritos ao registrador.

Atraves do registrador interno e possıvel programar algumas opcoes do ST7538,como a frequencia de portadora entre 60kHz ate 132.5kHz, velocidade da taxa detransmissao, distancia em marca e espaco, etc.

3.1.1.4 Funcoes Auxiliares

O ST7538 apresenta uma serie de perifericos para auxiliar o controle e a operacaodo chip, entre eles podemos destacar:

Detector de Banda em Uso: Detecta quando uma frequencia da configuracaoatual do modem e detectada na linha, esta condicionado ao detector de porta-dora para prevenir alarmes falsos. E diferente do detector de portadora devidoa sensibilidade dos seus filtros.

Timeout de Transmissao: Apos um ou 3 segundos de transmissao contınua, oST7538 e forcado para o modo de recepcao conforme o tempo programado noseu registrador interno. Pode se utilizado para controlar o acesso ao meio.

Wacthdog de Hardware: O ST7538 possui um watchdog que previne falhas desoftware assertando o pino RST causando o reset externo e interno da placa.Pode ser programado no registrador interno.

Detector de Cruzamento por Zero: E um detector de cruzamento por zero nalinha de energia eletrica. Atraves do registrador interno e possıvel sincronizara transmissao com o cruzamento para diminuir os pacotes perdidos.

Divisor de Clock: E possıvel colocar um dos clocks divididos internamente noST7538 conforme o programado no registrador interno.

Protetor de Temperatura: Desliga uma parte do chip ao detectar que a tempe-ratura dele passou de um determinado limite.

Regulador de Tensao: Gera uma tensao de 5V para a parte digital do circuito epossui um comparador para verificar a qualidade dessa tensao.

Page 34: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

32

3.1.2 Componente Analogicos

Para garantir o funcionamento do ST7538 foi construıdo uma interface analogicapara o modem. Essa interface e bem similar ao Apliccation Notes do ST7538 (CAN-TONE, 2003) e e composta basicamente pelos seguintes componentes:

Alimentacao Digital: E constituıdo de um regulador de tensao 7805 que gera aalimentacao de 5V para a parte digital do circuito. A alimentacao digital podeser selecionada dele ou do ST7538 segundo um jumper.

Gerador de Clock: E constituıdo de um cristal de 16MHz e seus capacitores paragerar o clock de 16 MHz para o ST7538.

Filtros Passa Banda: Sao filtros sintonizados nas frequencias possıveis de trans-missao. Sao tres filtros, um na entrada do sinal antes do transformador dedesacoplamento, um antes da recepcao e outro antes da transmissao. Os fil-tros foram especificados para uma portadora de 132,5kHz.

Circuito de Protecao: Composto de tres diodos zeners de 6V8 rapidos que pro-tegem o circuito contra spikes da rede eletrica.

Controle de Corrente: Composto de um resistor e um capacitor, serve para con-trolar o valor maximo da corrente nos drivers de saıda do ST7538.

Controle de Tensao: Composto de um divisor de tensao, serve para manter atensao constante no pino Vsense, esse controle e utilizado para ajustar o ganhodo sinal de saıda do ST7538. Atraves de um jumper e possıvel escolher qualsera a linha amostrada.

Detector de Cruzamento por Zero: Composto de um filtro passa baixa paraeliminar as altas frequencias, pode ser conectado ao ST7538 atraves de umjumper na placa.

3.2 Parte Digital

A parte digital do hardware do modem e composta pela a interface dos sinas doST7538 com a porta paralela de um computador IBM-PC padrao.

3.2.1 Interface com a Porta Paralela

A porta paralela e uma interface de comunicacao entre um computador e umperiferico, que nesse caso sera o modem PLC. Ela e basicamente composta de tresregistradores: registrador de dados, registrador de status e registrador de controle.Cada registrador e composto de 8 bits, porem nem todos eles estao disponıveisexternamente na porta paralela. Neste trabalho ficou definido a utilizacao dos pinosde dados para enviar sinais para o modem, dos pinos de status para receber sinaise, por fim, do registrador de controle para as demais operacoes.

3.2.1.1 Pinos de Entrada

Atraves dos seguintes pinos e possıvel controlar o funcionamento do ST7538:

Page 35: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

33

REG/DATA: atraves desse pino e possıvel informar ao ST7538 se ha o envio da-dos para serem modulados e transmitidos na rede ou armazenados no seuregistrador interno, alterando o seu funcionamento.

TxD: Pino de entrada digital, atraves desse pino passa-se o dado para o chip.

Rx/Tx: atraves desse pino podemos alternar entre uma secao de transmissao ourecepcao de acordo com uma comunicacao do tipo half-duplex.

WD#: Pino de watchdog, esse pino e utilizado para resetar o contador interno doST7538 e impedir o reset no pino RESET# da placa.

Os pinos de entrada do ST7538 estao conectados ao registrador de dados da portaparalela atraves de um buffer tri-state que esta constantemente habilitado (tal buffere utilizado como uma forma de protecao) e a ligacao com os pinos e feita conformea Tabela 3.2.

Tabela 3.1: Ligacao do registrador de controle.Pino na porta paralela Bit do registrador

AUTOF1 bit1INIT bit2

Tabela 3.2: Ligacao dos pinos de entrada.Pino na porta paralela Bit do registrador Pino no ST7538

Data0 bit0 REG/DATAData1 bit1 TxDData2 bit2 Rx/TxData3 bit3 WD#

3.2.1.2 Pinos de Saıda

Os pinos de saıda apresentam diversos status do modem PLC que devem serconstantemente lidos para garantir o controle sobre o mesmo. Os seguintes pinossao lidos do ST7538:

RxD: Pino de saıda digital, atraves desse pino e possıvel receber os dados doST7538.

CLRT: Pino de clock digital, atraves desse pino e possıvel amostrar o pino RxD deforma correta.

MCLK: Pino de clock digital, esse pino possui um clock de alta frequencia quepode ser utilizado para realizar transferencias rapidas para o driver. O seuvalor depende do registrador interno do ST7538.

RESET#: Pino de reset, esse pino e utilizado para que o ST7538 resete o driverdevido a uma falha de software, caso o watchdog esteja habilitado.

Page 36: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

34

PG: Pino de status da alimentacao, esse pino avisa que a tensao de alimentacao doST7538 esta adequada atraves do nıvel logico 1.

TOUT: Pino de time-out, esse pino e assertado quando uma transmissao durarmais que o valor programado no registrador do ST7538. Esse pino tambempode representar que o chip esta numa temperatura acima do padrao e que aplaca deve ser desligada.

REG OK: Pino de status do registrador interno do ST7538, esse pino garante queo conteudo do registrador interno do chip e valido.

CP/PD: Pino de deteccao de portadora, atraves desse pino o ST7538 indica apresenca de uma portadora ou preambulo (alternancia de marcas e espacos)na linha de energia eletrica, conforme o valor do registrador interno do ST7538.

BU: Pino de banda em uso, similar ao CP/PD porem com outra sensibilidade.

ZCOUT: Pino de deteccao de cruzamento por zero na linha de energia, pode serutilizado para sincronizar a transmissao com a rede de energia.

Devido ao fato de que o ST7538 possui mais pinos de saıda que o registrador destatus, eles estao conectados ao registrador compartilhando um barramento atravesde tres buffers tri-state, que sao habilitados pelo registrador de controle da portaparalela conforme a o bit desejado para a leitura. Os pinos desse registrador estaoconectados conforme a Tabela 3.1. A ligacao com os pinos de status e feita conformea Tabela 3.3.

Tabela 3.3: Ligacao dos pinos de saıda.

Pino na porta paralela Bit do registrador Pinos no ST7538

ST1 ST2 ST3ERROR bit3 CLRT ZCOUT TOUTACK bit6 RESET# RESET# RESET#SLCT bit4 RxD REG OK BUBUSY1 bit7 CD/PD PG MCLK

3.2.1.3 Pinos de Controle

Como dito anteriormente os pinos de status sao lidos atraves de um barramento,logo e necessario utilizar os pinos de controle para setar o buffer que utilizara obarramento conforme a Tabela 3.1.

A Tabela 3.4 indica como os valores do registradores de controle afetam o bar-ramento de status.

1Pino invertido

Page 37: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

35

Tabela 3.4: Ligacao nos multiplexadores.AUTOF INIT ST Ativo

0 0 ST30 1 NenhumSTAtivo1 0 ST11 1 ST2

3.3 Mudanca de Hardware

Atraves do desenvolvimento do software do modem foi constatada a necessidadeda mudanca no hardware. Para efetuar o recebimento de dados no processador docomputador seria necessario ficar realizando polling no ST7538, sendo que tal fatoiria consumir muito tempo de processamento dificultando a utilizacao do modemjuntamente com o kernel do Linux. Para corrigir esse defeito foi conectado o pinode clock diretamente no pino 10 (ACK) da porta paralela e a partir deste momentofoi utilizado uma interrupcao para o recebimento de dados, melhorando o processo.A Tabela 3.5 apresenta a nova configuracao dos pinos.

Tabela 3.5: Ligacao dos pinos de saıda.Pino na porta paralela Bit do registrador Pinos no ST7538

ST1 ST2 ST3ERROR bit3 RESET# ZCOUT TOUTACK bit6 CLRT CLRT CLRTSLCT bit4 RxD REG OK BUBUSY1 bit7 CD/PD PG MCLK

Page 38: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

36

Page 39: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

37

4 SOFTWARE DO MODEM PLC

Neste capıtulo serao expostos os passos da construcao do software desenvolvidopara controlar o modem PLC. Para que sejam compreendidas os passos descritosneste relatorio, e conveniente que haja uma breve explicacao dos conceitos envolvi-dos nas atividades. Uma pequena introducao sobre sistemas operacionais, particu-larmente sobre o sistema Linux, sera fornecida. Tambem, uma breve descricao dalinguagem C utilizada no desenvolvimento do software do modem PLC. A seguirsera explicado o desenvolvimento do software do modem PLC.

4.1 Plataforma de Desenvolvimento do Software

Como opcoes para plataforma de desenvolvimento do driver para computado-res IBM-PC ha como principais alternativas o Windows e o Linux. No Windowsnao ha acesso aos codigos fontes do sistema operacional e exitem algumas dificulda-des de acessar alguns dispositivos de I/O como a porta paralela, por exemplo. Jana plataforma Linux, os codigos fontes do mesmo sao acessıveis, o que possibilitaqualquer alteracao que podera eventualmente ser necessaria, e existe acesso a todosos dispositivos do computador. Por esse motivo o Linux e utilizado amplamenteno desenvolvimento de aplicacoes embarcadas, tornando-se a plataforma de desen-volvimento ideal para o driver tanto para o seu desenvolvimento como para a suaaplicacao.

Com a escolha da plataforma de desenvolvimento do driver, torna-se necessa-rio a escolha do tipo de driver que sera desenvolvido. Inicialmente pode-se pensarem implementar um simples driver para dispositivos de caracter para Linux. Aprimeira vista essa alternativa e uma boa opcao pois implementacao de um driverchar e simples e facil de ser compreendida evitando maiores problemas. Driverschar funcionam de forma similar a arquivos em linguagem C. Porem pelo fato deque o modem implementa uma comunicacao serial, a implementacao de tal driverimplicaria na necessidade de desenvolvimento de complexos programas de aplicacoespara a sua utilizacao. Para realizar comunicacao serial torna-se adequando o desen-volvimento de um driver TTY para Linux pois tal driver apresenta certas funcoesadequadas a essa comunicacao e possuem uma serie de programas ja prontos para asua utilizacao. Porem ele apresenta uma implementacao um pouco mais complexaque um driver char. Assim o desenvolvimento de um driver TTY torna-se adequadonessa situacao para tornar o resultado mais util ao usuario final.

Devido ao fato de que o modem realiza uma comunicacao half-duplex torna-senecessario implementar algum controle de acesso a linha de transmissao no driverpara evitar que dois modems tentem transmitir ao mesmo tempo, o que consequente-

Page 40: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

38

mente inviabilizaria a comunicacao. Nesse trabalho sera implementado um controlede fluxo CSMA-ppersitente, pois ele possui as caracterısticas necessarias para essaaplicacao e permite uma maior eficiencia do meio de transmissao.

4.1.1 Sistemas Operacionais

Em um sistema microprocessado complexo, varios processos sao executados. Emum sistema multitarefa, o processador deve atender a todos os processos em umintervalo reduzido de tempo, de forma que para um usuario do sistema, eles parecamser executados simultaneamente. Todos os processos demandam recursos do sistema,como memoria, conexao de rede, tempo de computacao, acesso a dispositivos etc.O sistema operacional pode ser entendido como uma camada de software, colocadaentre a aplicacao e o hardware, responsavel por gerenciar a execucao dos processose suas demandas.

Na maior parte do tempo, o processador executa aplicativos. O sistema opera-cional e chamado na ocorrencia de um evento especial, que pode ser uma chamadade sistema ou uma interrupcao de periferico. A chamada de sistema merece atencaoespecial, pois e a interface atraves da qual o aplicativo faz uma solicitacao de servicoao sistema operacional. Uma chamada de sistema e como uma chamada de sub-rotina, mas que transfere a execucao para o sistema operacional. Ao seu retorno, aexecucao do aplicativo e retomada na instrucao seguinte a chamada.

A parte do sistema operacional responsavel pela implementacao das chamadasde sistema e chamada de nucleo (kernel) do sistema. O kernel gerencia o acesso aoprocessador, a memoria, aos dispositivos de entrada e saıda e ao sistema de arquivos.

4.1.1.1 Linux

O sistema operacional Linux e um sistema Unix-like, tendo muitas caracterısticascomuns a outros sistemas UNIX. Uma virtude do sistema Linux e ser um sistemaoperacional independente de plataforma, ou seja, nao esta atrelado a processadoresIntel e compatıveis. O sistema Linux pode ser utilizado, por exemplo, em processa-dores ARM, Alpha, PowerPC, M68K, MIPS, SPARC, S390 e outros, que constituemem uma alternativa a arquitetura IA32 de processadores (RUBINI; CORBET, 2001).

Em um sistema Unix-like, existem diversas tarefas sendo executadas. Cada tarefarequisita por recursos do sistema operacional, tais como, processamento, memoria,conectividade de rede e outros recursos especıficos. O kernel do Linux e um grandeprograma que se encarrega de distribuir tais recursos entre as tarefas. O kernel podeser dividido, como mostra a Figura 4.1, nas seguintes partes:

Gerenciador de Tarefas: O kernel e encarregado de destruir e criar tarefas, bemcomo tratar da sua conexao com o mundo externo. A comunicacao entrediferentes tarefas e a base para o correto funcionamento do sistema operacionale tambem e tratado pelo kernel. Ainda existe o escalonador (schelduler), quecontrola como as tarefas dividem o processamento da CPU nesta parte dokernel.

Gerenciador de Memoria: A memoria do computador e um recurso essencial, ea polıtica para o seu uso e crucial para o desempenho do sistema operacional.O kernel constroi um espaco de enderecamento virtual para si e para todas astarefas. Atraves de tal enderecamento e feita a divisao entre kernel space e user

Page 41: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

39

Figura 4.1: Divisao do kernel do Linux.

space. O sistema operacional, que roda no kernel space, gerencia a memoriacontra acessos nao autorizados. Esta tarefa de protecao e feita com o auxilio dohardware do processador. De fato, todos os processadores modernos possuemnıveis (ou modalidades) diferentes de operacao, e algumas acoes apenas saopermitidas em certos nıveis de operacao.

Sistemas de Arquivo: Unix e fortemente baseado na conceito de sistemas de ar-quivo. Praticamente tudo em um sistema Unix pode ser tratado como umarquivo. O kernel constroi um sistema de arquivos estruturados em cima deuma hardware desestruturado, e o resultado dessa abstracao e usado por todoo sistema. Ainda. o Linux suporta multiplos sistemas de arquivo de diferentestipos, sendo que, cada tipo de sistema utilizada uma forma diferente de orga-nizar os dados no meio fısico. Como exemplo de sistemas de arquivos existem:EXT2, EXT3, FAT, NTFS, JFFS2 e Reiser.

Controlador de Dispositivos: Cada operacao do sistema operacional, geralmente,e direcionada para um dispositivo fısico diferente. Com excecao do processa-dor, memoria, e outras poucas entidades, todas as operacoes direcionadas aum hardware sao executadas por um codigo especıfico para aquele dispositivo.Tal codigo e chamado de driver. O kernel deve ter um driver especıfico paracada periferico presente no sistema, desde HD ate o teclado. Por esse motivosera desenvolvido um driver para tratar as funcoes do modem PLC. Existemdiversos tipos de drivers, porem eles nao serao abordados nesse trabalho.

Sistema de Rede: As operacoes de rede devem ser controladas pelo sistema ope-

Page 42: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

40

racional porque tais operacoes nao sao uma especificas a uma tarefa. Pacoteschegando sao eventos assıncronos que devem ser coletados, identificados e des-pachados antes que uma tarefa precise deles. O sistema e responsavel porentregar os pacotes a sua devida tarefa, ou a sua interface de rede, e controlara execucao dos programas de acordo com a sua atividade na rede. Uma grandevantagem do Linux, e que o seu kernel ja possui desenvolvido controles de di-versos protocolos como PPP, AX-25, FR, SLIP, PLIP, X25 e outros. Assimtais protocolos sao gerenciados pelo kernel sendo transparentes para o modemPLC.

Neste ponto sera implementado um driver TTY no kernel do Linux, controladopelo controlador de dispositivos, que utilizara as operacoes de rede para realizar acomunicacao entre dois modems.

4.2 Linguagem C

A linguagem C tem sido utilizada com sucesso nas mais diversas aplicacoes, taiscomo sistemas operacionais, compiladores e utilitarios em geral. A popularidadeda linguagem aumentou nos ultimos anos, tornando C uma linguagem de propositogeral. O codigo C e bastante portavel. Isso significa que softwares escritos paraum determinado processador podem ser facilmente transmitidos para um outro dearquitetura diferente.

O sistema operacional UNIX, do qual o Linux e derivado, e escrito em C. Naverdade, a linguagem C foi desenvolvida para que se implementasse o sistema UNIX.Portanto, e natural que C seja a linguagem utilizada para o desenvolvimento deaplicativos para o sistema Linux.

A principal diferenca entre a linguagem C e as outras linguagens de programacaoreside no emprego de ponteiros, ou seja, no enderecamento indireto. Os programassao estruturados atraves da definicao e chamada de funcoes. Dados podem serarmazenados em arranjos (arrays) ou estruturas.

Na comparacao com outras linguagens, C e considerado de nıvel medio, poisoferece um bom grau de abstracao ao programador (caracterıstica de linguagens dealto nıvel), ao mesmo tempo em que permite acesso eficiente ao hardware (como emlinguagens de baixo nıvel) (SCHILDT, 1988).

Distribuicoes Linux oferecem um compilador C, o GCC, que foi o compiladorutilizado para gerar o driver.

4.2.1 Programando no Kernel do Linux

Esta parte tem como objetivo introduzir alguns conceitos de programacao para okernel do linux. Todas as funcoes utilizadas no drivers podem ser achadas no codigofonte do kernel -2.6.17 localizados nos headers do mesmo.

Inicialmente como o kernel roda por si mesmo, ele nao pode utilizar funcoes dabibliotecas padroes do C e sim as suas proprias. A funcao printf() e substituıdapela printk() que esta definida no kernel de modo bastante similar ao printf().O driver pode utilizar printk() pois, apos ele ser linkado com o kernel, ele temacesso a todas os sımbolos publicos do kernel (funcoes e variaveis).

Indo adiante, existem outras diferencas entre o driver do kernel e uma apli-cacao. Onde uma aplicacao faz uma simples tarefa do inıcio ao fim, um driver

Page 43: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

41

deve-se registrar para prover futuras chamados do kernel ao mesmo. A funcaoinit_module() e as outras chamadas por ela sao responsaveis pela inicializacao dodriver e por informar ao kernel a sua existencia e as suas funcionalidades. A funcaocleanup_module() faz o contrario.

E possıvel adicionar um driver ao kernel ja carregado utilizando os programasinsmod e rmmod para adicionar e retirar, respectivamente, drivers e modulos nokernel. Deve-se executar tais programas como superusuario para que a operacaoseja feita com sucesso. A Figura 4.2 mostra como as funcoes e os ponteiros saoutilizados para adicionar uma nova funcionalidade ao kernel rodando.

Figura 4.2: Adicionando um Driver no Kernel.

Como mencionado anteriormente, a divisao do espaco do usuario e do kernel eimplementada pelo controlador de memoria, e assim deve-se usar funcoes especıficaspara tratar ponteiros recebidos do programa de aplicacao. A funcao put_user() eutilizada para colocar dados no espaco do usuario e get_user() recebe dados domesmo.

Somente funcoes que sao realmente parte do kernel podem ser usada no driver.Qualquer header pode ser encontrado em include/linux e include/asm dentro daarvore do kernel.

Como ate mesmo o menor driver e linkado a todo o kernel do Linux, deve-setomar o cuidado no uso de variaveis globais. Caso existam duas variaveis globaiscom o mesmo nome, podem ocorrer os mais variados tipos de falhas, que dependeraoda configuracao do computador usado e dos modulos carregados no kernel. Paraevitar tais problemas recomenda-se a utilizacao da declaracao static para as suasvariaveis.

Page 44: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

42

Por fim, uma ultima diferenca em a programacao no kernel e na aplicacao e arespeito de erros no programa. Enquanto um uma falha de segmentacao na aplicacaoe simplesmente um aviso e um debbuger pode ser utilizado para rastrear o problema,no kernel, tal problema e fatal nao so para a aplicacao que esta sendo executadae sim para todo o sistema. Metodos de depuracao para o kernel existem, poremeles nao serao tratados neste trabalho. No desenvolvimento do driver foi utilizadoo programa oops para depurar tais problemas.

4.3 Especificacoes de Hardware

Apos apresentar alguns conceitos a respeito de Linux e possıvel tratar do de-senvolvimento do driver em si. O hardware construıdo influencia fortemente o fun-cionamento do driver, pois algumas opcoes do registrador interno do ST7538 naopoderao ser alteradas pelos usuarios do modem. As opcoes fixas sao as seguintes:

Frequencia da portadora: Os filtros localizados no hardware da placa foram sin-tonizados para um frequencia de portadora de 132.5KHz. Logo essa opcao ficafixa.

Watchdog: Nao sera implementada uma funcao de watchdog nesta versao de soft-ware e portanto ele deve permanecer desabilitado.

Timeout: O timeout de transmissao sera implementado em software nas funcoesde protocolo, logo o de hardware deve ser desabilidato.

Deteccao de portadora: Como sera utilizado o pino de clock para interromper oprocessador, o clock so devera ser passado para o processador quando houveralgum dado disponıvel tornando essa opcao fixa.

Interface Sıncrona: Nao sera implementada um software que suporte a interfaceassıncrona, logo essa opcao sera fixa.

Modo Pacote: Essa funcao foi retirada na primeira revisao do datasheet do ST7538e nao sera desenvolvido suporte para ela.

As demais opcoes como taxa de transmissao e outras ficam como opcoes defuncionamento para o usuario.

4.4 Kernel Space: Driver TTY

Como citado em 4.1.1.1, foi implementado um driver TTY no kernel do Linuxpara realizar a parte de baixo nıvel do modem PLC. Ele devera se comunicar como usuario com um programa de aplicacao conforme os passos descrito em 4.5. Elepossui basicamente as seguintes funcoes:

1. funcoes de acesso ao hardware,

2. funcoes de protocolos,

3. funcoes de drivers TTY.

O codigo fonte do driver se encontra em anexo no final desse trabalho.

Page 45: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

43

4.4.1 Funcoes de Acesso ao Hardware

As funcoes dessa secao serao responsaveis por realizar o acesso em mais baixonıvel ao hardware do modem PLC. Elas deverao dar suporte as funcoes das camadassuperiores para o gerenciamento do driver.

4.4.1.1 Funcao de Leitura dos Pinos do ST7538

Para realizar a leitura e a escrita nos pinos de dados e de controles do ST7538basta realizar a leitura dos pinos, modificar os pinos desejados e entao escrever ovalor no respectivo registrador.

Para realizar a leitura dos pinos de status do ST7538 sera criada a funcaoreadMSR(), que deve seguir o seguir o fluxograma da Figura 4.3.

Figura 4.3: Fluxograma da leitura e escrita de dados no ST7538.

Primeiramente e necessario setar um dos buffers do barramento, realizar a leiturados seus pinos de status, armazenar em um registrador. Entao se deve setar outrobuffer e fazer todos os passos novamente. Assim e realizada a leitura dos pinos destatus do ST7538.

Page 46: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

44

4.4.1.2 Funcao de Leitura e Escrita do Registrador Interno do ST7538

Essas funcoes devem ser responsaveis por ler e escrever a configuracao desejadano registrador interno do ST7538. Isso e feito atraves da Figura 4.4

Figura 4.4: Sequencia de escrita no registrador interno do ST7538.

Devido ao fato de que a configuracao do modem nao e realizada durante o funcio-namento do driver, ou nao frequentemente, implementamos funcoes que realizam pol-ling no pino de clock do ST7538. As funcoes readRegister() e writeRegister()

implementam os passos do fluxograma da Figura 4.5 para fazer sua funcao.Inicialmente e necessario setar o pino REG/DATA do ST7538, a seguir deve-se

ler e escrever os proximos 24 bits de configuracao do modem PLC.A Figura 4.6 mostra a visualizacao de uma leitura do registrador interno do

ST7538 realizada pelo driver. Nota-se o clock variando rapidamente em baixo e obits no registrador sendo escritos em cima.

4.4.1.3 Funcao de leitura e escrita dados do ST7538.

Essas funcoes devem ser responsaveis por enviar e receber dados do ST7538. Ospassos para o recebimento dos dados sao similares a escrita e a leitura do registradorinterno do ST7538, porem ela e realizada de uma forma totalmente diferente das an-teriores. O recebimento/escrita de dados e realizada em um contexto de interrupcaodo processador e seguem os passos do fluxograma representado na Figura 4.7.

A cada ciclo de clock recebido, um dado e lido/escrito para o ST7538. A cadaoito bits que isso ocorre um novo dado e carregado para repetir o processo ou umdado e enviado para a camada superior do driver.

A Figura 4.8 mostra um frame sendo enviado para o hardware do modem PLC.Inicialmente um preambulo e enviado para que o ST7538 possa detectar a portadorana recepcao e assim garantir que nenhum bit sera perdido..

4.4.2 Funcoes de Protocolos

Para aumentar a robustez do sistema serao implementados dois nıveis de proto-colos: HLDC e CSMA-ppersistente. Por uma questao de facilidade para o usuariosera implementado tambem o protocolo KISS como explicado em 4.5.

4.4.2.1 Funcoes de HDLC

HDLC e um dos protocolos mais utilizados da camada dois do modelo OSI (TA-NENBAUM, 2003), sendo estruturado em frames. A Figura 4.9 mostra a estrutura

Page 47: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

45

Figura 4.5: Fluxograma do software de leitura e escrita de dados no ST7538.

Figura 4.6: Leitura do registrador interno do ST7538 realizada pelo driver.

Page 48: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

46

Figura 4.7: Fluxograma do software de leitura e escrita de dados no ST7538.

Figura 4.8: Envio de dados para o ST7538.

Page 49: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

47

de um frame HDLC.

Figura 4.9: Estrutura do Frame HDLC.

HDLC utiliza insercao/remocao de zeros (bit-stuffing) para garantir que no pa-cote de dados nao apareca a flag (7E), que por sua vez serve para delimitar o inıcioe o final de cada frame. E possıvel visualizar a transmissao dos flags para o modemna Figura 4.10 e na Figura 4.11. Apesar de o frame original HDLC possuir umcampo de endereco, esse nao sera utilizado neste projeto em questao. Isso se deveao fato de que o campo de endereco so e realmente util quando existe um hardwareque permite a decodificacao do endereco, o que nao se aplica a esse caso. Por issoa transmissao sera broadcast, ou seja, todos os modems escutam todos os modemse uma camada de cima devera ser responsavel por descartar as mensagens que naosao para aquele modem.

Figura 4.10: Envio da sequencia de flags para o ST7538.

Apos acabar o pacote o software devera adicionar 16 bits de CRC para garantira integridade dos dados durante a transmissao. Para as funcoes de CRC foramincluıdos trechos de codigos prontos. Se durante a mensagem ocorre a presenca desete bits ”um”consecutivos o frame e abortado.

A funcao hdlc_tx_byte() e responsavel pela a maquina de estados da trans-missao do frame HDLC, isto e, pela insercao de flags, do CRC e do bit-stuffing datransmissao. A funcao hdlc_rx_byte() e responsavel pela a recepcao do frame, epela verificacao do bit-stuffing durante o frame, os flags sao tratados pela a funcao

Page 50: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

48

Figura 4.11: Envio da sequencia de flags para o ST7538 (zoom).

hdlc_rx_add_bytes() e o CRC pela a funcao hdlc_rx_flag(). Essa ultima funcaotambem coloca o dado em um pacote KISS como sera explicado em 4.4.2.3.

4.4.2.2 Funcoes de CSMA-ppersistente

O CSMA, e um protocolo de telecomunicacao que organiza a forma como osmodems compartilham um determinado meio de transmissao. Este protocolo estabaseado na deteccao da portadora para controlar quando o meio esta sendo utilizado.O CSMA-ppersistente e uma variacao do CSMA na qual ao detectar que o meio estadisponıvel para a transmissao, cada modem tem uma probabilidade p de ganhar oacesso ao meio. Utilizando tal tecnica e possıvel diminuir o numero de colisoes deacordo com uma rede especifica em questao.

Como o modem PLC utiliza uma comunicacao half-duplex e necessario realizarum controle de acesso ao meio fısico, ou seja, a linha de energia eletrica. O acessoao meio sera realizado atraves de timeslots, assim cada modem PLC tera um de-terminado tempo para verificar o estado do meio. Caso o meio esteja livre, ele irarealizar um sorteio para tentar ganhar o meio.

Ao final de cada transmissao do frame o meio sera liberado para o sorteio entreos modems escutando a linha. Cada modem tambem devera enviar uma certa quan-tidade de flags apos o final e antes do inıcio da mensagem. Todos esses parametrossao passados para o driver na sua insercao junto ao kernel de Linux.

Para realizar o controle de timeslots sera inicializado no kernel um timer queperiodicamente ira verificar o estado da linha e caso ela esteja livre, cada modem queesta disputando o meio tera uma probabilidade P de conseguir pegar o meio parasi. Caso dois ou mais modem peguem a linha ao mesmo tempo, ocorrera colisaoe assim sendo que nenhum dos modems conseguira transmitir corretamente o seu

Page 51: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

49

pacote. Como nao e possıvel detectar colisoes com o hardware do modem PLC, naosera feito nenhum controle de transmissao nesse nıvel do driver. O nıvel de cimadevera ser responsavel por controlar se o frame foi entregue ou se ocorreu colisao.

A funcao arb_sched() e responsavel por verificar a disponibilidade do meio aofinal de cada timeslot e caso necessario chamar a funcao tx_arbitrate() ondeocorre o sorteio do acesso ao meio. O sorteio e feito atraves da funcao rand()

implementada de (METAWARE, 1994).

4.4.2.3 Funcoes de KISS

O protocolo KISS foi proposto em 1986 por Phil Karn (KARN, 1986) para o seusoftware TCP-IP. Esse protocolo tambem e baseando em frames, sendo que o inıcioe o final de cada frame e seguido de um caractere especial chamado FEND similarao flag do HDLC, porem nenhum controle de erro como CRC e implementado.

Se o caractere FEND aparece durante os dados, ele e convertido em dois caracte-res especiais em sequencia, FESC TEND. Assim como se o FESC aparece nos dados,ele e convertido em FESC TFESC. O caractere FEND nao e enviado entre os dadospara garantir a integridade dos frames. Esse protocolo e bastante similar ao SLIP.

Para distinguir comandos de dados, o primeiro byte de cada frame indica o tipode frame que sera. A Tabela 4.1 indica o tipo de frame em funcao do comando.

Tabela 4.1: Tipo de frame do protocolo KISS.

Valor do bit Tipo de Frame Funcao

0x0 DATA FRAME o resto do frame contem dadosa ser passados para o proximo nıvel

0x1 TXDELAY o proximo byte contem o txdelay em 10ms0x2 P o proximo byte contem o parametro ppersistente0x3 TIMESLOT o proximo byte contem o timeslot em 10ms0x4 TXTAIL o proximo byte contem o txtail em 10ms

Esses parametros foram descritos anteriormente em 4.4.2.2 e podem ser alteradoscom o driver ja carregado atraves do protocolo KISS. A funcao lpmodem_put_char()e responsavel por realizar a decodificao dos caracteres especiais bem como os ma-cros ADD_CHAR() e ADD_KISSCHAR() por codificar os mesmos. A funcao lpmodem_put_fend() devera decodificar o comando do protocolo KISS fazendo a alteracao do pa-rametro quando necessario. Por fim as funcoes store_kiss_packet(), ack_packet(),get_packet() e store_packet() fazem a interface com o buffer circular em que osdados vindo desse protocolo sao colocados para serem encapsulados em HDLC. Aquestao do uso do protocolo KISS sera explicada no decorrer desse trabalho em 4.5.

4.4.3 Funcoes de Driver TTY

Como comentado anteriormente a interface do modem PLC sera implementadaem plataforma Linux atraves de um driver TTY. Basicamente o driver TTY devepossuir as seguintes funcoes:

Funcoes de Inicializacao e Finalizacao: Tais funcoes sao chamadas na iniciali-zacao/finalizacao do driver, elas devem alocar memorias para buffers, realizar a

Page 52: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

50

configuracao default do modem, requisitar a porta paralela, registrar o driver,etc.

Funcao de Ioctl: Essa e a forma basica do usuario modificar a configuracao domodem. Atraves dele devera ser possıvel escrever e ler a configuracao doregistrador interno do ST7538, configurar os parametros do protocolo KISS,etc.

Funcoes de Escrita: O driver deve receber dados atraves dessas funcoes. Devido aforma que ele esta implementado e necessario que esse dado esteja encapsuladocom o protocolo KISS.

Funcoes de Leitura: Essa e a forma que o driver envia dados para o usuario. Osdados serao enviados utilizando protocolo KISS.

Funcoes de Buffers: Essas funcoes devem responder ao usuario informacoes sobreos buffers, tais como quantos caracteres estao neles ou quanto espaco ha livrepara aceitar novos caracteres.

4.5 User Space: kissattach

A escolha de um driver TTY para o kernel do Linux foi feita especialmentepensando nesta parte do trabalho. Devido a grande utilidade de tais drivers para acomunicacao serial, ja foram desenvolvidos programas de aplicacao que se conectama eles, implementado assim um modem completo.

O kissattach faz parte do pacote ax25-util utilizado por radios amadores, de-senvolvido para funcionar com o kernel do Linux. AX.25 e um protocolo para redesde radio amador, que nesse caso e utilizado para um rede PLC, ele deve ter o seusuporte compilado juntamente com o as operacoes de redes ,citadas em 4.1.1.1. dokernel para que o programa de aplicacao seja suportado. No caso de um radio ama-dor a comunicacao tambem e half-duplex, onde o ar e o meio fısico e somente umradio pode falar de cada vez no meio, por essa similaridade com a comunicacao PLCtal protocolo tambem sera utilizado no modem PLC.

Atraves desse programa e possıvel emular um ponto IP, utilizando controle defluxo TCP, no computador. Esse programa se conecta a um driver TTY do Linuxutilizando o protocolo KISS. e assim, justifica-se a utilizacao de um driver TTYjuntamente com o protocolo KISS na implementacao do modem. Devido ao fato deque ele utiliza o protocolo TCP-IP, nao precisamos nos preocupar com a questao deenderecamento ou de controle de colisao pois tal protocolo ira descartar pacotes quenao sejam para si e tambem ira se comprometer a retransmitir pacotes perdidos.Assim sera possıvel emular uma placa de rede atraves do modem PLC, porem valeressaltar que a velocidade sera baixa.

Como dito anteriormente, para utilizar o kissattach e necessario ter compiladoo kernel com suporte ao protocolo AX.25, ter instalado a biblioteca ax25 e possuiro pacote de utilidade ax25. Tais pacotes podem ser encontrados em (BAECHLE;OSTERRIED, 2006). Inicialmente deve-se configurar o arquivo /etc/ax25/axports

conforme a Figura 4.12. Tal arquivo contem informacoes sobre configuracao do meiofısico como:

name e um identificar para o meio,

Page 53: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

51

Figura 4.12: Configuracao do Arquivo /etc/ax25/axports.

callsign contem uma identificacao para radio amador e nao precisa ser alterada,

speed e a velocidade do meio e deve ser setada de acordo com o que o modemsuporta,

paclen e o tamanho maximo do pacote ax25 a ser transmitido,

windows, e o tamanho da janela do protocolo ax25, ou seja, quantos pacotes ele iraenviar anter de receber uma confirmacao,

description bem e a descricao.

Como o arquivo /etc/ax25/axports configurado e possıvel executar o programada aplicacao. A Figura 4.13 demonstra um exemplo de como executar o programa.Os argumentos sao os seguintes:

Figura 4.13: Utilizando o kissattach.

/dev/ttyPLC e o device associado ao major do driver TTY,

Page 54: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

52

1 identificador que esta no arquivo /etc/ax25/axports,

192.168.0.1 endereco IP.

Apos esses passos, pode-se confirmar a configuracao e o endereco IP utilizando ocomando ifconfig conforme a Figura 4.14. Para verificar o estado do link utilizamoso comando ping conforme a Figura 4.15 Assim podemos afirmar que o kissattach

esta utilizando as funcoes do kernel para empacotar os dados com TCP/IP sobreAX.25 e apos sobre KISS. A seguir o driver do Linux e chamado juntamente com asfuncoes de protocolo kiss para se comunicar com as funcoes de protocolo HDLC. Es-sas ultimas funcoes serao chamadas quando as funcoes de CSMA permitirem o acessoao meio. Por fim, apos os dados serem encapsulados em HDLC as funcoes de acessoao hardware sao chamadas passando a informacao para o modem e completando ociclo.

Figura 4.14: Display do ifconfig.

A Figura 4.16 mostra a transmissao para o hardware dos dados encapsuladoscom o protocolo HDLC oriundos do comando ping direcionado para a rede criadacom o programa kissattach. Pode-se perceber a colocacao de flags do HDLC antesdos dados passados para o driver. A Figura 4.17 apresenta a recepcao do frame peloo modem receptor, de forma semalhente a transmissao como deveria ocorrer.

Page 55: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

53

Figura 4.15: Modem Funcionando.

Figura 4.16: Transmissao de dados para o modem PLC.

Page 56: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

54

Figura 4.17: Recepcao de dados pelo o modem PLC.

Page 57: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

55

5 CONCLUSAO

Os conhecimentos adquiridos durante os tres anos de trabalho como bolsistade iniciacao cientifica foram fundamentais para a consolidacao do aprendizado dafaculdade e foram responsaveis pelo desenvolvimento do esquematico e da placa PCBdo modem PLC. A figura 5.1 mostra o modem PLC desenvolvido nesse trabalho

Figura 5.1: Foto do Modem PLC.

No desenvolvimento do software a escolha dos protocolos para a comunicacaoPLC foi adequada, tendo em vista que mesmo com a grande quantidade de ruıdo narede eletrica a comunicacao ocorre sem maiores problemas. O protocolo HDLC, como conceito de frames, bit-stuffing, e CRC permite a eliminacao de pacotes errados e oprotocolo TCP/IP e responsavel por retransmitir tais pacotes, bem como cuidar doenderecamento. O controle de acesso ao meio CSMA-ppersistente se mostrou umaboa alternativa para uma rede PLC.

Page 58: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

56

Uma questao que poderia ser repensada seria o fato de ter utilizado um micropro-cessador menor no controle do modem PLC, tal modificacao evitaria o ocorrenciasde interrupcoes no computador e aumentaria o desempenho do mesmo. Porem talcaminho nao foi adotado devido ao fato de diminuir a complicacao em hardware eresolver em software os problemas, o fato do driver esta funcionando tambem indicaque a escolha tomada foi viavel. Uma futura alteracao que pode ser proposta nestetrabalho e a implementacao de um driver de rede para Linux.

Tendo em vista que o objetivo era aprender a usar o ST7538, os resultados foramsatisfatorios. Por fim, vale ressaltar que o modem PLC desenvolvido neste relatorio eapenas um prototipo inicial, e como tal esta sujeito a uma serie de BUGs que so seraodescobertos e possivelmente corrigidos no decorrer da utilizacao do mesmo. Nestetrabalho foi construıda uma rede PLC composta de dois modems e conseguimoscomunicar um com o outro atraves do comando ping. Esse trabalho fica disponıvela todas as pessoas que quiserem formar um rede PLC com as ressalvas acima.

Page 59: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

57

REFERENCIAS

BAECHLE, R.; OSTERRIED, T. AX.25 Utilities for Hamradio. Disponıvel em:<http://ax25.sourceforge.net/>. Acesso em: 04 dez. 2006.

CANTONE, G. ST7538 FSK Power Line Transceiver Demo-Kit Descrip-tion. STMicroeletronics, 2003. Application Note.

IGUACU ENERGIA, L. Projeto PLC. Disponıvel em: <http://www.ienergia.

com.br>. Acesso em: 04 dez. 2006.

KARN, P. Proposed Raw TNC Functional Spec. Santa Cruz, California, EUA,1986.

MAJUMDER, A.; CAFFREY, J. Power Line Communications: an overview.IEEE Potentials, vol.23, 2004.

METAWARE. Metaware High C Reference Manual. Santa Cruz, California,EUA, 1994.

NETWORKING, P. Introduction to Powerline Communications. Disponı-vel em: <http://plc.qcslink.com/IntroPLC/PLCmain.htm>. Acesso em: 04 dez.2006.

REDES, P. de. Redes PLC. Disponıvel em: <http://www.projetoderedes.com.br/tutoriais/tutorial_redes_plc_01.php>. Acesso em: 04 dez. 2006.

RUBINI, A.; CORBET, J. Linux Devices Driver. 2th.ed. Sebastopol, 2001.

SCHILDT, H. Turbo C, Guia do Usuario. 2th.ed. Sao Paulo, 1988.

TANENBAUM, A. Redes de Computadorees. 3th.ed. 2003.

Page 60: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

58

Page 61: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

59

APENDICE A DIAGRAMAS ESQUEMATICOS DO MO-

DEM PLC

Page 62: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

60

Page 63: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

61

Page 64: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

62

Page 65: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

63

APENDICE B ARTE FINAL DO PCB DO MODEM PLC

Page 66: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

64

Page 67: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

65

Page 68: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

66

Page 69: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

67

Page 70: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

68

Page 71: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

69

APENDICE C CODIGO FONTE DO DRIVER DO MO-

DEM PLC

/*******************************************************************************

lpmodem Driver

Walter Fetter Lages <[email protected]>

Diego Caberlon Santini <[email protected]>

This program is free software; you can redistribute it and/or modify

it under the terms of the GNU General Public License as published by

the Free Software Foundation; either version 2 of the License, or

(at your option) any later version.

This program is distributed in the hope that it will be useful,

but WITHOUT ANY WARRANTY; without even the implied warranty of

MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

GNU General Public License for more details.

You should have received a copy of the GNU General Public License

along with this program; if not, write to the Free Software

Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Command line options (insmod command line)

major

major number the driver should use; default 124

parport port? [,port? [,port? [,port? ] ] ]

parport number to use

defbaud baud? [,baud? [,baud? [,baud? ] ] ]

default baud rate; default 2400

tx_delay delay? [,delay? [,delay? [,delay? ] ] ]

transmitter keyup delay in 10 ms; default 20

tx_tail tail? [,tail? [,tail? [,tail? ] ] ]

transmitter tail time in 10 ms; default 2

slottime time? [,time? [,time? [,time? ] ] ]

transmitter slot time in 10 ms; default 10

ppersist persist? [,persist? [,persist? [,persist? ] ] ]

tranmitter p persistence scaled to 0..255 range; default 63

fulldup duplex? [,duplex? [,duplex? [,duplex? ] ] ]

full duplex; default 0

History:

2006.06.19 Start development based on BIM 3.0.0

*******************************************************************************/

#include <linux/module.h>

#include <asm/io.h>

#include <asm/uaccess.h>

#include <linux/delay.h>

#include <linux/errno.h>

#include <linux/fs.h>

#include <linux/interrupt.h>

#include <linux/ioport.h>

#include <linux/mm.h>

#include <linux/sched.h>

#include <linux/workqueue.h>

#include <linux/tty.h>

#include <linux/tty_flip.h>

#include <linux/parport.h>

#include <st7538.h>

#include <lpmodem.h>

//lpmodem defines

Page 72: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

70

#define PLC_DEBUG 2

#define PLC_IRQ 0x07

#define PLC_IRQ_ENABLE 0x10

#define writeMCR(handle,mcr) parport_write_data(handle->dev->port,mcr)

#define readMCR(handle,mcr) mcr = parport_read_data(handle->dev->port)

#define plc_timeout(ti) if(jiffies >= (ti+5)) return -EIO;

//no parport

#ifdef NOPAR

#undef parport_read_control

#undef parport_write_control

#define parport_read_control(Q) inb(0x37a)

#define parport_write_control(Q, data) outb(data,0x37a)

#undef parport_read_data

#undef parport_write_data

#define parport_read_data(Q) inb(0x378)

#define parport_write_data(Q, data) outb(data,0x378)

#undef parport_read_status

#define parport_read_status(Q) inb(0x379)

#endif

//Debug stuff

#if PLC_DEBUG > 0

#define DEBUGPLCF(arg,arg1) printk(arg,arg1)

#else

#define DEBUGPLCF(arg,arg1)

#endif

#if PLC_DEBUG > 0

#define DEBUGPLC1 printk

#if PLC_DEBUG > 1

#define DEBUGPLC2 printk

#else

#define DEBUGPLC2

#endif

#else

#define DEBUGPLC1

#endif

#ifndef KISS_VERBOSE

#define KISS_VERBOSE

#endif

#define LPMODEM_TYPE_NORMAL 0

#define TTY_DRIVER_TYPE_LPMODEM 6

#define BUFLEN_RX 8192

#define BUFLEN_TX 8192

#define NR_PORTS 4

#define LPMODEM_MAGIC 0x4c504d31

/**lpmodem Modem Status Register Address

*/

enum MSR_REGS

{

/* OE378#=0=>D5=0, IRQ7EN=0=>D4=0, SLCTIN#=1=>D3=0, STROBE#=1=>D0=0 */

MSR0 =0x02, /* A1=AUTOF#=0=>D1=1, A0=INIT#=0=>D2=0 */

MSR1 =0x06, /* A1=AUTOF#=0=>D1=1, A0=INIT#=1=>D2=1 */

MSR2 =0x00, /* A1=AUTOF#=1=>D1=0, A0=INIT#=0=>D2=0 */

NOMSR =0x04, /* A1=AUTOF#=1=>D1=0, A0=INIT#=1=>D2=1 */

MSRMASK =0x06 /* MASK for A1 e A0*/

};

#define LPMODEM_EXTENT 3

/* Modem state flags */

#define ARB_TIMER_ON 0x01

/* default baud rate */

#define DFLT_BAUD 2400

static int major=124;

static int parport[4]={0xffffffff,0xffffffff,0xffffffff,0xffffffff};

static int defbaud[4]={DFLT_BAUD,DFLT_BAUD,DFLT_BAUD,DFLT_BAUD};

static int tx_delay[4]={20,20,20,20};

static int tx_tail[4]={2,2,2,2};

static int slottime[4]={10,10,10,10};

static int ppersist[4]={63,63,63,63};

static int fulldup[4]={0,0,0,0};

MODULE_AUTHOR("Diego Caberlon Santini <[email protected]>");

MODULE_DESCRIPTION("lpmodem Driver");

MODULE_SUPPORTED_DEVICE("ttyPLC");

module_param(major,int, 0);

MODULE_PARM_DESC(major,"major number the driver should use; default 124");

module_param_array(parport,int,NULL,0);

MODULE_PARM_DESC(parport,"port? [,port? [,port? [,port? ] ] ] parport to use; 0=parport0, 1=parport1, 2=parport2, 0xffffffff=none");

module_param_array(defbaud,int,NULL,0);

MODULE_PARM_DESC(defbaud,"baud? [,baud? [,baud? [,baud? ] ] ] default baud rate; default 2400");

module_param_array(tx_delay,int,NULL,0);

Page 73: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

71

MODULE_PARM_DESC(tx_delay,"delay? [,delay? [,delay? [,delay? ] ] ] transmitter keyup delay in 10 ms; default 20");

module_param_array(tx_tail,int,NULL,0);

MODULE_PARM_DESC(tx_tail,"tail? [,tail? [,tail? [,tail? ] ] ] transmitter tail time in 10 ms; default 2");

module_param_array(slottime,int,NULL,0);

MODULE_PARM_DESC(slottime,"time? [,time? [,time? [,time? ] ] ] transmitter slot time in 10 ms; default 1000");

module_param_array(ppersist,int,NULL,0);

MODULE_PARM_DESC(ppersist,"persist? [,persist? [,persist? [,persist? ] ] ] tranmitter p persistence scaled to 0..255 range; default 63");

module_param_array(fulldup,int,NULL,0);

MODULE_PARM_DESC(fulldup,"duplex? [,duplex? [,duplex? [,duplex? ] ] ] full duplex; default 0");

enum BH_SCHED_BITS

{

BH_SCHED_RX= 0x01,

BH_SCHED_TX= 0x02

};

struct access_params

{

int tx_delay; /* the transmitter keyup delay in 10ms units */

int tx_tail; /* the transmitter keyoff delay in 10ms units */

int slottime; /* the slottime in 10ms; usually 10 = 100ms */

int ppersist; /* the p-persistence 0..255 */

int fulldup; /* the driver does not support full duplex, setting

this just makes the driver send even if DCD is on */

};

#define BH_RX_BUFLEN 256 /* bottom half RX buffer */

#define BH_TX_BUFLEN 16 /* bottom half TX buffer */

struct hdlc_state_rx

{

int rx_state; /* 0 = sync hunt, != 0 receiving */

unsigned int bitstream; /* keep track of bitstream to catch stuffed bit */

unsigned int bitbuf; /* hdlc input bit buffer */

int numbits; /* number of bits at bitbuf */

unsigned char bh_buffer[BH_RX_BUFLEN]; /* bottom-half buffer */

int bh_wr; /* bottom-half buffer write point */

int bh_rd; /* bottom-half buffer read point */

int bh_buflen; /* bottom-half buffer lenght; redundant but simpler */

int len; /* buffer lenght in use */

unsigned char *bp; /* buffer pointer */

unsigned char buffer[LPMODEM_MAXFLEN+2]; /* make room for CRC */

};

struct hdlc_state_tx

{

int tx_state; /* 0=send flags,1=send txtail(flags),2=send packet */

int numflags; /* number of flags to send */

unsigned int bitstream; /* keep track of bit stream to insert stuff bit */

unsigned char ptt; /* push to talk state */

unsigned char holdptt; /* flag to release ptt after transmit */

unsigned int bitbuf; /* buffer for hdlc output bits */

int numbits; /* number of bits available at bitbuf */

unsigned char bh_buffer[BH_TX_BUFLEN]; /* bottom-half buffer */

int bh_wr; /* bottom-half buffer write point */

int bh_rd; /* bottom-half buffer read point */

int bh_buflen; /* bottom-half buffer lenght; redundant but simpler */

int len; /* frame buffer lenght in use */

unsigned char *bp; /* frame buffer pointer */

unsigned char buffer[LPMODEM_MAXFLEN+2]; /* make room for CRC */

};

struct modem_state

{

unsigned char dcd;

unsigned int flags;

};

struct packet_buffer

{

unsigned int rd;

unsigned int wr;

unsigned int buflen;

unsigned char *buffer;

};

struct packet_hdr

{

unsigned int next;

unsigned int len;

/* packet follows */

};

#ifdef LPMODEM_DEBUG

struct bit_buffer

Page 74: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

72

{

unsigned int rd;

unsigned int wr;

unsigned char buffer[64];

};

struct debug_vals

{

unsigned long last_jiffies;

unsigned cur_intcnt;

unsigned last_intcnt;

};

#endif /* LPMODEM_DEBUG */

struct kiss_decode

{

unsigned char dec_state; /* 0 = hunt FEND */

unsigned char escaped;

unsigned char pkt_buf[LPMODEM_MAXFLEN+1];

unsigned int wr;

};

struct lpmodem_state

{

int magic;

unsigned int parport;

int dflt_baud;

int opened;

struct tty_struct *tty;

struct pardevice *dev;

#ifdef LPMODEM_USE_BH

struct work_struct tq_receiver;

struct work_struct tq_transmitter;

#endif /* LPMODEM_USE_BH */

struct packet_buffer rx_buf;

struct packet_buffer tx_buf;

struct access_params ch_params;

struct hdlc_state_rx hdlc_rx;

struct hdlc_state_tx hdlc_tx;

struct modem_state modem;

struct timer_list arb_timer;

#ifdef LPMODEM_DEBUG

struct bit_buffer bitbuf_hdlc;

struct debug_vals debug_vals;

#endif /* LPMODEM_DEBUG */

struct kiss_decode kiss_decode;

struct lpmodem_statistics stat;

int preamble;

};

static struct

{

int parport;

int baud;

struct access_params dflt_ch_params;

} lpmodem_ports[NR_PORTS];

static struct tty_struct *lpmodem_ttys[NR_PORTS];

static struct termios *lpmodem_termios[NR_PORTS];

static struct termios *lpmodem_termios_locked[NR_PORTS];

static int lpmodem_refcount;

static struct tty_driver lpmodem_driver;

static struct lpmodem_state lpmodem_state[NR_PORTS];

static unsigned char rx_bit_buff;

static unsigned char tx_bit_buff;

static unsigned char rxcont;

static unsigned char txcont;

static unsigned char plc_fixme;

static unsigned char plc_fixme2;

static unsigned int aaa=0;

static void load_txbitbuff(struct lpmodem_state *lpmodem);

/*******************************************************************************

The CRC routines are stolen from WAMPES by Dieter Deyke

*******************************************************************************/

Page 75: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

73

static const unsigned short crc_ccitt_table[]=

{

0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,

0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,

0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,

0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,

0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,

0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,

0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,

0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,

0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,

0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,

0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,

0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,

0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,

0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,

0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,

0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,

0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,

0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,

0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,

0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,

0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,

0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,

0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,

0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,

0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,

0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,

0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,

0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,

0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,

0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,

0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,

0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78

};

static void append_crc_ccitt(unsigned char *buffer,int len)

{

unsigned short crc=0xffff;

DEBUGPLCF("%s\n",__FUNCTION__);

for(;len > 0;len--) crc=(crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];

crc^= 0xffff;

*buffer++=crc;

*buffer++=crc >> 8;

}

static int check_crc_ccitt(const unsigned char *buf,int cnt)

{

unsigned short crc=0xffff;

DEBUGPLCF("%s\n",__FUNCTION__);

for(; cnt > 0; cnt--) crc=(crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];

return (crc & 0xffff) == 0xf0b8;

}

static int baud_table[]=

{

0,50,75,110,134,150,200,300,600,1200,1800,2400,4800,

9600,19200,38400,57600,115200

};

static int baud(struct lpmodem_state *lpmodem)

{

unsigned int i=lpmodem->tty->termios->c_cflag & CBAUD;

DEBUGPLCF("%s\n",__FUNCTION__);

if(i & CBAUDEX)

{

i&=~CBAUDEX;

i+=15;

}

return baud_table[i];

}

static inline unsigned int tenms_to_flags(struct lpmodem_state *lpmodem,unsigned int tenms)

{

return (tenms*baud(lpmodem)+999)/1000;

}

/*******************************************************************************

KISS decoder functions

*******************************************************************************/

static int store_packet(struct packet_buffer *buf,unsigned char *data,char from_user,unsigned int len)

{

struct packet_hdr *hdr;

unsigned int needed=sizeof(struct packet_hdr)+len;

Page 76: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

74

int err;

unsigned int free=buf->rd-buf->wr;

DEBUGPLCF("%s\n",__FUNCTION__);

if(buf->rd <= buf->wr)

{

free=buf->buflen-buf->wr;

if((free < needed) && (buf->rd >= needed))

{

hdr=(struct packet_hdr *)(buf->buffer+buf->wr);

hdr->next=0;

hdr->len=0;

buf->wr=0;

free=buf->rd;

}

}

if(free < needed) return 0; /* buffer overrun */

hdr=(struct packet_hdr *)(buf->buffer+buf->wr);

if(from_user)

{

if((err=copy_from_user(hdr+1,data,len))) { return err;}

}

else memcpy(hdr+1,data,len);

hdr->len=len;

hdr->next=buf->wr+needed;

if(hdr->next+sizeof(struct packet_hdr) >= buf->buflen) hdr->next=0;

buf->wr=hdr->next;

return 1;

}

static void get_packet(struct packet_buffer *buf,unsigned char **data,unsigned int *len)

{

struct packet_hdr *hdr;

DEBUGPLCF("%s\n",__FUNCTION__);

*data=NULL;

*len=0;

if(buf->rd == buf->wr) return;

hdr=(struct packet_hdr *)(buf->buffer+buf->rd);

while(!(hdr->len))

{

buf->rd=hdr->next;

if (buf->rd == buf->wr) return;

hdr=(struct packet_hdr *)(buf->buffer+buf->rd);

}

*data=(unsigned char *)(hdr+1);

*len=hdr->len;

}

static void ack_packet(struct packet_buffer *buf)

{

struct packet_hdr *hdr;

DEBUGPLCF("%s\n",__FUNCTION__);

if (buf->rd == buf->wr) return;

hdr=(struct packet_hdr *)(buf->buffer+buf->rd);

buf->rd=hdr->next;

}

static int store_kiss_packet(struct packet_buffer *buf,unsigned char *data,unsigned int len)

{

unsigned char *bp=data;

int ln=len;

/* variables of buf */

unsigned int rd;

unsigned int wr;

unsigned int buflen;

unsigned char *buffer;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!len || !data || !buf) return 0;

buflen=buf->buflen;

rd=buf->rd;

wr=buf->wr;

buffer=buf->buffer;

#define ADD_CHAR(c) {\

buffer[wr++] = c;\

if (wr >= buflen) wr = 0;\

if (wr == rd) return 0;\

}

#define ADD_KISSCHAR(c) {\

if (((c) & 0xff) == KISS_FEND) {\

ADD_CHAR(KISS_FESC);\

ADD_CHAR(KISS_TFEND);\

} else if (((c) & 0xff) == KISS_FESC) {\

ADD_CHAR(KISS_FESC);\

ADD_CHAR(KISS_TFESC);\

} else {\

ADD_CHAR(c);\

}\

Page 77: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

75

}

ADD_CHAR(KISS_FEND);

ADD_KISSCHAR(KISS_CMD_DATA);

for(;ln > 0;ln--,bp++) ADD_KISSCHAR(*bp);

ADD_CHAR(KISS_FEND);

buf->wr=wr;

#undef ADD_CHAR

#undef ADD_KISSCHAR

return 1;

}

static void lpmodem_put_fend(struct lpmodem_state *lpmodem)

{

DEBUGPLCF("%s\n",__FUNCTION__);

if(lpmodem->kiss_decode.wr <= 0 || (lpmodem->kiss_decode.pkt_buf[0] & 0xf0) != 0) return;

switch (lpmodem->kiss_decode.pkt_buf[0] & 0xf)

{

case KISS_CMD_DATA:

{

DEBUGPLC2("store packet\n");

if(lpmodem->kiss_decode.wr <= 8) break;

if(!store_packet(&lpmodem->tx_buf,lpmodem->kiss_decode.pkt_buf+1,0,lpmodem->kiss_decode.wr-1)) lpmodem->stat.tx_bufferoverrun++;

break;

}

case KISS_CMD_TXDELAY:

{

if(lpmodem->kiss_decode.wr < 2) break;

lpmodem->ch_params.tx_delay=lpmodem->kiss_decode.pkt_buf[1];

#ifdef KISS_VERBOSE

printk(KERN_INFO "lpmodem: TX delay = %ums\n",lpmodem->ch_params.tx_delay * 10);

#endif /* KISS_VERBOSE */

break;

}

case KISS_CMD_PPERSIST:

{

if(lpmodem->kiss_decode.wr < 2) break;

lpmodem->ch_params.ppersist=lpmodem->kiss_decode.pkt_buf[1];

#ifdef KISS_VERBOSE

printk(KERN_INFO "lpmodem: p-persistence = %u\n",lpmodem->ch_params.ppersist);

#endif /* KISS_VERBOSE */

break;

}

case KISS_CMD_SLOTTIME:

{

if(lpmodem->kiss_decode.wr < 2) break;

lpmodem->ch_params.slottime=lpmodem->kiss_decode.pkt_buf[1];

#ifdef KISS_VERBOSE

printk(KERN_INFO "lpmodem: slottime = %ums\n",lpmodem->ch_params.slottime*10);

#endif /* KISS_VERBOSE */

break;

}

case KISS_CMD_TXTAIL:

{

if(lpmodem->kiss_decode.wr < 2) break;

lpmodem->ch_params.tx_tail=lpmodem->kiss_decode.pkt_buf[1];

#ifdef KISS_VERBOSE

printk(KERN_INFO "lpmodem: TX tail = %ums\n",lpmodem->ch_params.tx_tail*10);

#endif /* KISS_VERBOSE */

break;

}

case KISS_CMD_FULLDUP:

{

if(lpmodem->kiss_decode.wr < 2) break;

lpmodem->ch_params.fulldup=lpmodem->kiss_decode.pkt_buf[1];

#ifdef KISS_VERBOSE

printk(KERN_INFO "lpmodem: %s duplex\n",lpmodem->ch_params.fulldup ? "full" : "half");

#endif /* KISS_VERBOSE */

break;

}

default:

{

#ifdef KISS_VERBOSE

printk(KERN_INFO "lpmodem: unhandled KISS packet code %u\n",lpmodem->kiss_decode.pkt_buf[0] & 0xf);

#endif /* KISS_VERBOSE */

break;

}

Page 78: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

76

}

}

/******************************************************************************

lpmodem hardware access function

*******************************************************************************/

static int writeTX(struct lpmodem_state *handle, unsigned char data)

{

DEBUGPLCF("%s\n",__FUNCTION__);

if(handle == NULL) return -EINVAL;

writeMCR(handle, (data & MCR_TXD));

return 0;

}

static int readRX(struct lpmodem_state *handle)

{

unsigned char status;

unsigned char control;

DEBUGPLCF("%s\n",__FUNCTION__);

if(handle==NULL) return -EINVAL; /* Invalid argument */

control = parport_read_control(handle->dev->port);

control &= ~MSRMASK;

parport_write_control(handle->dev->port,MSR0 | control);

status=parport_read_status(handle->dev->port);

parport_write_control(handle->dev->port,NOMSR | control);

return ((status & PLC_MSR_RXD) >> 4);

}

static int readMSR(struct lpmodem_state *handle,plcMSR *msr)

{

unsigned char status;

unsigned char control;

DEBUGPLCF("%s\n",__FUNCTION__);

if(handle==NULL) return -EINVAL; /* Invalid argument */

control = parport_read_control(handle->dev->port);

control &= ~MSRMASK;

parport_write_control(handle->dev->port,MSR0 | control);

status=parport_read_status(handle->dev->port);

/* BUSY signal in conector is inverted with respect to the

#BUSY bit (bit 7) in status register */

*msr=(plcMSR)(status ^ 0x80);

parport_write_control(handle->dev->port,MSR1 | control);

status=parport_read_status(handle->dev->port);

/* BUSY signal in conector is inverted with respect to the

#BUSY bit (bit 7) in status register*/

*msr|=(PLC_MSR_PG | PLC_MSR_REGOK_ | PLC_MSR_TOUT | PLC_MSR_RESET_) & (((plcMSR)(status ^ 0x80))<<8);

parport_write_control(handle->dev->port,MSR2 | control);

status=parport_read_status(handle->dev->port);

/* BUSY signal in conector is inverted with respect to the

#BUSY bit (bit 7) in status register*/

*msr|=(PLC_MSR_CD_PD | PLC_MSR_BU | PLC_MSR_ZCOUT | PLC_MSR_RESET_) & (((plcMSR)(status ^ 0x80))<<16);

parport_write_control(handle->dev->port,NOMSR | control);

return 0;

}

static int readRegister(struct lpmodem_state *handle,st7538reg *reg)

{

plcMSR status=0;

int n, count;

unsigned long ti;

st7538reg regtmp=0;

int error;

DEBUGPLCF("%s\n",__FUNCTION__);

if(handle==NULL) return -EINVAL; /* Invalid argument */

*reg=1;

disable_irq(PLC_IRQ);

/* Read now */

writeMCR(handle,MCR_RXTX | MCR_REG_DATA | MCR_WD_);

DEBUGPLC1("Lendo Registrador interno:");

Page 79: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

77

for(n=23;n >= 0;n--)

{

ti=jiffies;

count=0;

/* RXD data is available on the rising edge of CLRT. Wait for it */

do

{

if((error=readMSR(handle,&status))) return error;

plc_timeout(ti)/* I/O error */

count++;

}

while( !(status & PLC_MSR_CLRT) );

/* Too fast, this isnt a data*/

if(count == 1)

n++;

if (n !=24){

if((error=readMSR(handle,&status))) return error;

regtmp=(status & PLC_MSR_RXD) >> 4; /* RXD is bit 4 */

DEBUGPLC2("%ld", (status & PLC_MSR_RXD) >> 4);

regtmp<<=n;

*reg|=regtmp;

}

/* RXD data is available on the rising edge of CLRT.

Wait for the high state to prepare for the read of the next bit */

ti=jiffies;

do

{

if((error=readMSR(handle,&status))) return error;

if(jiffies >=(ti+2) ) return -EIO; /* I/O error */

}

while(status & PLC_MSR_CLRT);

}

DEBUGPLC1(":0x%lx\n",*(unsigned long int *)reg);

/* Back to rx mode */

writeMCR(handle,MCR_RXTX | MCR_WD_);

enable_irq(PLC_IRQ);

return 0;

}

static int writeRegister(struct lpmodem_state *handle,st7538reg *reg)

{

st7538reg buffer;

unsigned long ti;

int n;

plcMSR status;

int error;

DEBUGPLCF("%s\n",__FUNCTION__);

if(handle==NULL) return EINVAL; /* Invalid argument */

DEBUGPLC1("Escrevendo no registrador:");

DEBUGPLC1("0x%lx:\n", *reg);

disable_irq(PLC_IRQ);

/* Set what we need */;

writeMCR(handle,MCR_REG_DATA | MCR_WD_);

/* Give it a time..., the critical point is the end, not the begin*/

msleep(100);

for(n=23;n >= 0;n--)

{

ti=jiffies;

/* TXD is loaded on the rising edge of CLRT. Wait for the low state */

do

{

if((error=readMSR(handle,&status))) return error;

plc_timeout(ti) /* I/O error */

}

while( (status & PLC_MSR_CLRT) );

buffer=*reg >> n; /* shift the bit to transmit to bit 0 */

buffer<<=1; /* TXD is bit 1 */

buffer=(buffer & MCR_TXD) | MCR_REG_DATA | MCR_WD_;

writeMCR(handle,buffer);

DEBUGPLC2("%d",(unsigned)((buffer & MCR_TXD) >> 1));

ti=jiffies;

/* TXD is loaded on the rising edge of CLRT. Wait for it. */

do

{

if((error=readMSR(handle,&status))) return error;

plc_timeout(ti) /* I/O error */

}

while(!(status & PLC_MSR_CLRT));

}

Page 80: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

78

/* This is important because the register is write now */

writeMCR(handle,MCR_RXTX);

msleep(100);

writeMCR(handle,MCR_RXTX | MCR_WD_);

DEBUGPLC1("\n");

enable_irq(PLC_IRQ);

return 0;

}

/*******************************************************************************

Tx Arbitration functions

*******************************************************************************/

/*

* ANSI sample implementation borrowed from Metaware High C Reference Manual

* ANSI uses 1 as seed. Here seed is get from jiffies to be more, well, random.

*/

static unsigned long int rand_seed;

static inline int rand(void)

{

rand_seed=1103515245UL*rand_seed+12345;

return (unsigned int) (rand_seed/65536) % 32768;

}

inline unsigned char preamble_tx(struct lpmodem_state *lpmodem)

{

if(lpmodem->preamble < 0) return 0;

return (lpmodem->preamble--)? 0xaa:0xff;

}

static inline void ptt(struct lpmodem_state *lpmodem,int value)

{

// unsigned char ier;

// unsigned int mcr=inb(MCR(lpmodem->iobase));

if(value)

{

// outb(mcr | RTS,MCR(lpmodem->iobase)); /* set RTS */

lpmodem->hdlc_tx.holdptt=1;

lpmodem->hdlc_tx.ptt=1;

lpmodem->stat.ptt_keyed++;

lpmodem->preamble=(3*baud(lpmodem)+9999)/10000;

/* enable TX interrupt */

DEBUGPLC2("Enable TX\n");

load_txbitbuff(lpmodem);

txcont++;

plc_fixme = 0;

writeMCR(lpmodem, (((tx_bit_buff >> (8 - txcont)) << 1) & MCR_TXD));

}

else

{

lpmodem->hdlc_tx.ptt=0;

/* disable tx interrupt */

DEBUGPLC2("Diseble TX\n");

writeMCR(lpmodem,MCR_RXTX);

lpmodem->hdlc_tx.holdptt=0;

}

}

static inline void tx_arbitrate(struct lpmodem_state *lpmodem)

{

unsigned char *bp;

unsigned int len;

if(!lpmodem || lpmodem->hdlc_tx.ptt || lpmodem->modem.dcd) {

return;}

get_packet(&lpmodem->tx_buf,&bp,&len);

if(!bp || !len) return;

if(!lpmodem->ch_params.fulldup)

{

if((rand() % 256) > lpmodem->ch_params.ppersist) return;

}

lpmodem->hdlc_tx.tx_state=0;

if(!(lpmodem->hdlc_tx.numflags=tenms_to_flags(lpmodem,lpmodem->ch_params.tx_delay))) lpmodem->hdlc_tx.numflags=1;

lpmodem->hdlc_tx.numbits=lpmodem->hdlc_tx.bitbuf=lpmodem->hdlc_tx.bitstream=0;

ptt(lpmodem,1);

}

static inline void tx_release(struct lpmodem_state *lpmodem)

{

lpmodem->hdlc_tx.tx_state=1;

if(!(lpmodem->hdlc_tx.numflags=tenms_to_flags(lpmodem,lpmodem->ch_params.tx_tail))) lpmodem->hdlc_tx.numflags=1;

if(lpmodem->hdlc_tx.numbits % 8) lpmodem->hdlc_tx.numflags++;

}

static inline void rx_arbitrate(struct lpmodem_state *lpmodem)

{

Page 81: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

79

if(!lpmodem || !lpmodem->hdlc_tx.ptt) return;

if(!lpmodem->ch_params.fulldup)

{

if((rand() % 256) > lpmodem->ch_params.ppersist) return;

tx_release(lpmodem);

}

}

static void arb_sched(unsigned long data)

{

struct lpmodem_state *lpmodem=(struct lpmodem_state *)data;

unsigned long ms;

static unsigned char rxcont_bak;

plcMSR msr;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!lpmodem || lpmodem->magic != LPMODEM_MAGIC) return;

ms=lpmodem->ch_params.slottime*10;

del_timer(&lpmodem->arb_timer);

lpmodem->arb_timer.expires=jiffies+(ms*HZ+999)/1000+1;

add_timer(&lpmodem->arb_timer);

/* if transmitting don’t even test DCD */

if(lpmodem->hdlc_tx.ptt) return;

readMSR(lpmodem, &msr);

lpmodem->modem.dcd = (msr & PLC_MSR_CD_PD);

if( ~(lpmodem->modem.dcd & PLC_MSR_CD_PD) & (rxcont == rxcont_bak)){

plc_fixme2=1;

}

rxcont_bak = rxcont;

tx_arbitrate(lpmodem);

}

/*******************************************************************************

HDLC functions

********************************************************************************/

#ifdef LPMODEM_DEBUG

inline static void add_bitbuffer_byte(struct bit_buffer *buf,unsigned char byte)

{

buf->buffer[buf->wr]=byte;

buf->wr=(buf->wr+1)%sizeof(buf->buffer);

}

#endif /* LPMODEM_DEBUG */

/*

* This routine moves received bytes from rx buffer to flip buffer.

* The flip buffer is located in the tty structure, and is used as a high

* speed interface between the tty driver and the tty line discipline.

*/

static inline void rx_chars_to_flip(struct lpmodem_state *lpmodem)

{

unsigned int cnt;

unsigned int new_rd;

int i;

if(lpmodem->rx_buf.rd <= lpmodem->rx_buf.wr) cnt=lpmodem->rx_buf.wr-lpmodem->rx_buf.rd;

else cnt=lpmodem->rx_buf.buflen-lpmodem->rx_buf.rd;

for(i=0;i<cnt;i++){

// printk("recebi %x\t",((unsigned char*)(lpmodem->rx_buf.buffer+lpmodem->rx_buf.rd))[i]);

tty_insert_flip_char(lpmodem->tty, ((unsigned char*)(lpmodem->rx_buf.buffer+lpmodem->rx_buf.rd))[i], TTY_NORMAL);

}

tty_schedule_flip(lpmodem->tty);

/* updates rx buffer pointer */

new_rd=lpmodem->rx_buf.rd+cnt;

if(new_rd >= lpmodem->rx_buf.buflen) new_rd -= lpmodem->rx_buf.buflen;

lpmodem->rx_buf.rd=new_rd;

}

static inline int hdlc_rx_add_bytes(struct lpmodem_state *lpmodem,unsigned int bits,int num)

{

int added=0;

while(lpmodem->hdlc_rx.rx_state && num >= 8)

{

if((unsigned int)lpmodem->hdlc_rx.len >= sizeof(lpmodem->hdlc_rx.buffer))

{

/* buffer overflow */

lpmodem->hdlc_rx.rx_state=0;

return 0;

}

*lpmodem->hdlc_rx.bp++=bits >> (16-num);

lpmodem->hdlc_rx.len++;

num-=8;

added+=8;

}

Page 82: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

80

return added;

}

static inline void hdlc_rx_flag(struct lpmodem_state *lpmodem)

{

if(lpmodem->hdlc_rx.len < 4) return;

if(!check_crc_ccitt(lpmodem->hdlc_rx.buffer,lpmodem->hdlc_rx.len)) return;

lpmodem->stat.rx_packets++;

if(!store_kiss_packet(&lpmodem->rx_buf,lpmodem->hdlc_rx.buffer,lpmodem->hdlc_rx.len-2)) lpmodem->stat.rx_bufferoverrun++;

rx_chars_to_flip(lpmodem); /* send packet to flip buffer */

}

static void hdlc_rx_byte(struct lpmodem_state *lpmodem,unsigned int byte)

{

int i;

unsigned int mask1;

unsigned int mask2;

unsigned int mask3;

unsigned int mask4;

unsigned int mask5;

unsigned int mask6;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!lpmodem) return;

DEBUGPLC2("RECEBI RX: %x\n",byte);

byte&=0xff; /* mask unused bits */

#ifdef LPMODEM_DEBUG

add_bitbuffer_byte(&lpmodem->bitbuf_hdlc,byte);

#endif /* LPMODEM_DEBUG */

lpmodem->hdlc_rx.bitstream >>= 8;

lpmodem->hdlc_rx.bitstream |= byte << 8;

lpmodem->hdlc_rx.bitbuf >>= 8;

lpmodem->hdlc_rx.bitbuf |= byte << 8;

lpmodem->hdlc_rx.numbits+=8;

/* check stuffed bit */

for(i=7,mask1=0x1fc,mask2=0x1fe,mask3=0x0fc,

mask4=0x1f8,mask5=0xf8,mask6=0xff;

i >= 0;

i--,mask1 <<= 1,mask2 <<= 1,mask3 <<= 1,mask4 <<= 1,

mask5 <<= 1,mask6 = (mask6 << 1) | 1)

{

if((lpmodem->hdlc_rx.bitstream & mask1) == mask1) lpmodem->hdlc_rx.rx_state=0; /* abort received */

else if((lpmodem->hdlc_rx.bitstream & mask2) == mask3)

{

/* flag received */

if(lpmodem->hdlc_rx.rx_state)

{

/* terminate frame */

hdlc_rx_add_bytes(lpmodem,lpmodem->hdlc_rx.bitbuf << (8 + i),lpmodem->hdlc_rx.numbits-8-i);

hdlc_rx_flag(lpmodem);

}

/* start new frame */

lpmodem->hdlc_rx.len=0;

lpmodem->hdlc_rx.bp=lpmodem->hdlc_rx.buffer;

lpmodem->hdlc_rx.rx_state=1;

lpmodem->hdlc_rx.numbits=i;

}

else if((lpmodem->hdlc_rx.bitstream & mask4) == mask5)

{

/* stuffed bit */

lpmodem->hdlc_rx.numbits--;

lpmodem->hdlc_rx.bitbuf=(lpmodem->hdlc_rx.bitbuf & (~mask6)) | ((lpmodem->hdlc_rx.bitbuf & mask6) << 1);

}

}

lpmodem->hdlc_rx.numbits-=hdlc_rx_add_bytes(lpmodem,lpmodem->hdlc_rx.bitbuf,lpmodem->hdlc_rx.numbits);

}

static unsigned char hdlc_tx_byte(struct lpmodem_state *lpmodem)

{

unsigned char ret;

unsigned int mask1;

unsigned int mask2;

unsigned int mask3;

int i;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!lpmodem || !lpmodem->hdlc_tx.ptt) return 0;

for(;;)

{

/* if there are bits availabe at bitbuf return them */

if(lpmodem->hdlc_tx.numbits >= 8)

{

ret=lpmodem->hdlc_tx.bitbuf & 0xff;

lpmodem->hdlc_tx.bitbuf>>=8;

lpmodem->hdlc_tx.numbits-=8;

return ret;

}

switch(lpmodem->hdlc_tx.tx_state)

{

Page 83: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

81

default: /* reset */

{

ptt(lpmodem,0);

lpmodem->hdlc_tx.tx_state=0;

return 0;

}

case 0: /* send flags */

case 1: /* send txtail(flags) */

{

if(lpmodem->hdlc_tx.numflags)

{

/* insert flags into bitbuf */

lpmodem->hdlc_tx.numflags--;

lpmodem->hdlc_tx.bitbuf|=0x7e << lpmodem->hdlc_tx.numbits;

lpmodem->hdlc_tx.numbits+=8;

break;

}

if(lpmodem->hdlc_tx.tx_state == 1)

{

/* no more flags, reset ptt */

lpmodem->hdlc_tx.holdptt=0;

return 0;

}

/* get packet pointer and lenght */

get_packet(&lpmodem->tx_buf,&lpmodem->hdlc_tx.bp,(unsigned int *)&lpmodem->hdlc_tx.len);

if(!lpmodem->hdlc_tx.bp || !lpmodem->hdlc_tx.len)

{

/* no more packets */

tx_release(lpmodem);

break;

}

if(lpmodem->hdlc_tx.len >= LPMODEM_MAXFLEN)

{

/* packet too big, discard */

lpmodem->hdlc_tx.tx_state=0;

lpmodem->hdlc_tx.numflags=1;

ack_packet(&lpmodem->tx_buf);

/* chance to revert channel */

rx_arbitrate(lpmodem);

break;

}

/* copy packet to buffer */

memcpy(lpmodem->hdlc_tx.buffer,lpmodem->hdlc_tx.bp,lpmodem->hdlc_tx.len);

ack_packet(&lpmodem->tx_buf);

lpmodem->hdlc_tx.bp=lpmodem->hdlc_tx.buffer;

append_crc_ccitt(lpmodem->hdlc_tx.buffer,lpmodem->hdlc_tx.len);

/* the appended CRC */

lpmodem->hdlc_tx.len+=2;

lpmodem->hdlc_tx.tx_state=2; /* send packet */

lpmodem->hdlc_tx.bitstream=0;

lpmodem->stat.tx_packets++;

break;

}

case 2: /* send packet */

{

if(!lpmodem->hdlc_tx.len)

{

/* packet end */

lpmodem->hdlc_tx.tx_state=0;

lpmodem->hdlc_tx.numflags=1;

/* chance to revert channel */

rx_arbitrate(lpmodem);

break;

}

lpmodem->hdlc_tx.len--;

lpmodem->hdlc_tx.bitbuf |= *lpmodem->hdlc_tx.bp << lpmodem->hdlc_tx.numbits;

lpmodem->hdlc_tx.bitstream >>= 8;

lpmodem->hdlc_tx.bitstream |= (*lpmodem->hdlc_tx.bp++) << 16;

/* test if stuff bit is needed */

mask1=0x1f000;

mask2=0x10000;

mask3=0xffffffff >> (31-lpmodem->hdlc_tx.numbits);

lpmodem->hdlc_tx.numbits+=8;

for(i=0;i < 8;i++,mask1 <<= 1,mask2 <<= 1,mask3 = (mask3 << 1) | 1)

{

if((lpmodem->hdlc_tx.bitstream & mask1) != mask1) continue;

/* insert stuff bit */

lpmodem->hdlc_tx.bitstream&=~mask2;

lpmodem->hdlc_tx.bitbuf=(lpmodem->hdlc_tx.bitbuf & mask3) | ((lpmodem->hdlc_tx.bitbuf & (~mask3)) << 1);

lpmodem->hdlc_tx.numbits++;

mask3=(mask3 << 1) | 1;

}

break;

}

}

}

}

/*******************************************************************************

Interrupt & Bottom-half functions

*******************************************************************************/

#ifdef LPMODEM_DEBUG

Page 84: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

82

/*

* This routine measures how many interrupts are received per second.

*/

inline static void lpmodem_int_freq(struct lpmodem_state *lpmodem)

{

unsigned long cur_jiffies=jiffies;

/* measure the interrupt frequency */

lpmodem->debug_vals.cur_intcnt++;

if((cur_jiffies-lpmodem->debug_vals.last_jiffies) >= HZ)

{

lpmodem->debug_vals.last_jiffies=cur_jiffies;

lpmodem->debug_vals.last_intcnt=lpmodem->debug_vals.cur_intcnt;

lpmodem->debug_vals.cur_intcnt=0;

}

}

#endif /* LPMODEM_DEBUG */

static void load_txbitbuff(struct lpmodem_state *lpmodem)

{

#ifdef LPMODEM_USE_BH

int bh_sched = 0;

#endif /* LPMODEM_USE_BH */

DEBUGPLCF("%s\n",__FUNCTION__);

tx_bit_buff=preamble_tx(lpmodem);

if(!tx_bit_buff)

{

/* no more preamble */

#ifdef LPMODEM_USE_BH

/* check end of transmition */

if(!lpmodem->hdlc_tx.bh_buflen){

ptt(lpmodem,0);

}

tx_bit_buff=lpmodem->hdlc_tx.bh_buffer[lpmodem->hdlc_tx.bh_rd];

lpmodem->hdlc_tx.bh_rd=(lpmodem->hdlc_tx.bh_rd+1)%BH_TX_BUFLEN;

lpmodem->hdlc_tx.bh_buflen--;

bh_sched|=BH_SCHED_TX;

#else /* LPMODEM_USE_BH */

if(!lpmodem->hdlc_tx.holdptt){

ptt(lpmodem,0);

}

tx_bit_buff = hdlc_tx_byte(lpmodem);

#endif /* LPMODEM_USE_BH */

}

#ifdef LPMODEM_USE_BH

if(bh_sched & BH_SCHED_TX) schedule_work(&lpmodem->tq_transmitter);

#endif /* LPMODEM_USE_BH */

return;

}

irqreturn_t lpmodem_interrupt(int irq,void *dev_id,struct pt_regs *regs)

{

register struct lpmodem_state *lpmodem=(struct lpmodem_state *)dev_id;

// DEBUGPLC1("Interrupt\n");

#ifdef LPMODEM_USE_BH

int bh_sched;

#endif /* LPMODEM_USE_BH */

DEBUGPLCF("%s\n",__FUNCTION__);

if(lpmodem == NULL || lpmodem->magic != LPMODEM_MAGIC) return IRQ_NONE;

#ifdef LPMODEM_DEBUG

lpmodem_int_freq(lpmodem);

#endif /* LPMODEM_DEBUG */

#ifdef LPMODEM_USE_BH

bh_sched=0;

#endif /* LPMODEM_USE_BH */

if(!lpmodem->hdlc_tx.ptt)

{

if(aaa != jiffies){

rx_bit_buff |= (readRX(lpmodem) << (7 - rxcont));

rxcont++;

if((plc_fixme2 == 1) && (rxcont == 8))

{

if(rx_bit_buff != 0xff)

{

rx_bit_buff = (rx_bit_buff << 1);

rxcont--;

}

else

{

plc_fixme2 = 0;

}

// DEBUGPLC2("RECEBI RX: %x\n",rx_bit_buff);

Page 85: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

83

}

if(rxcont == 8)

{

rxcont =0;

#ifdef LPMODEM_USE_BH

if(lpmodem->hdlc_rx.bh_buflen >= BH_RX_BUFLEN)

{

/* bh buffer overflow */

#ifdef LPMODEM_DEBUG

printk("lpmodem: RX bottom-half buffer overflow\n");

#endif /* LPMODEM_DEBUG */

return -1;

}

lpmodem->hdlc_rx.bh_buffer[lpmodem->hdlc_rx.bh_wr]=rx_bit_buff;

lpmodem->hdlc_rx.bh_wr=(lpmodem->hdlc_rx.bh_wr+1)%BH_RX_BUFLEN;

lpmodem->hdlc_rx.bh_buflen++;

bh_sched|=BH_SCHED_RX;

#else /* LPMODEM_USE_BH */

hdlc_rx_byte(lpmodem,rx_bit_buff);

#endif /* LPMODEM_USE_BH */

// DEBUGPLC2("RECEBI RX: %x\n",rx_bit_buff);

rx_bit_buff= 0;

}

}}

else

{

if ( (plc_fixme == 0) || plc_fixme ==1){

plc_fixme++;

return IRQ_HANDLED;

}

else

{

if(aaa != jiffies){

// printk("%d",(inb(0x378) & MCR_TXD) >> 1);

txcont++;

writeTX(lpmodem,((tx_bit_buff >> (8 - txcont)) << 1));

if (txcont == 8)

{

load_txbitbuff(lpmodem);

txcont =0;

}

}

}

}

#ifdef LPMODEM_USE_BH

if(bh_sched & BH_SCHED_RX) schedule_work(&lpmodem->tq_receiver);

#endif /* LPMODEM_USE_BH */

aaa=jiffies;

return IRQ_HANDLED;

}

/* Bottom half */

#ifdef LPMODEM_USE_BH

static void bh_receiver(void *priv)

{

struct lpmodem_state *lpmodem=(struct lpmodem_state *)priv;

unsigned long flags;

spinlock_t lpmodem_lock=SPIN_LOCK_UNLOCKED;

unsigned char data;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!lpmodem || lpmodem->magic != LPMODEM_MAGIC) return;

while(lpmodem->hdlc_rx.bh_buflen)

{

spin_lock_irqsave(&lpmodem_lock,flags);

data=lpmodem->hdlc_rx.bh_buffer[lpmodem->hdlc_rx.bh_rd];

lpmodem->hdlc_rx.bh_rd=(lpmodem->hdlc_rx.bh_rd+1)%BH_RX_BUFLEN;

lpmodem->hdlc_rx.bh_buflen--;

spin_unlock_irqrestore(&lpmodem_lock,flags);

hdlc_rx_byte(lpmodem,data);

}

}

static void bh_transmitter(void *priv)

{

struct lpmodem_state *lpmodem=(struct lpmodem_state *)priv;

unsigned char data;

unsigned long flags;

spinlock_t lpmodem_lock=SPIN_LOCK_UNLOCKED;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!lpmodem || lpmodem->magic != LPMODEM_MAGIC) return;

while(lpmodem->hdlc_tx.bh_buflen < BH_TX_BUFLEN)

{

data=hdlc_tx_byte(lpmodem);

if(!lpmodem->hdlc_tx.holdptt) break;

Page 86: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

84

spin_lock_irqsave(&lpmodem_lock,flags);

lpmodem->hdlc_tx.bh_buffer[lpmodem->hdlc_tx.bh_wr]=data;

lpmodem->hdlc_tx.bh_wr=(lpmodem->hdlc_tx.bh_wr+1)%BH_TX_BUFLEN;

lpmodem->hdlc_tx.bh_buflen++;

spin_unlock_irqrestore(&lpmodem_lock,flags);

}

}

#endif /* LPMODEM_USE_BH */

/*******************************************************************************

Hardware initialization & cleanup functions

*******************************************************************************/

static void lpmodem_set_baud(struct lpmodem_state *lpmodem)

{

unsigned int baud=0;

unsigned int cflag;

unsigned int i;

st7538reg bbits=0;

st7538reg reg;

DEBUGPLCF("%s\n",__FUNCTION__);

if(lpmodem->parport==0xffffffff) return;

if(lpmodem->opened)

{

cflag=lpmodem->tty->termios->c_cflag;

/* can change only baud rate */

lpmodem->tty->termios->c_cflag=CS8 | CREAD | CLOCAL | (cflag & CBAUD);

i=cflag & CBAUD;

/* do not suport baud rates above 4800 */

if(i & CBAUDEX) lpmodem->tty->termios->c_cflag &=~CBAUDEX;

switch(i)

{

case B0:

baud=0;

break;

case B50:

case B75:

case B110:

case B134:

case B150:

case B200:

case B300:

case B600:

baud=600;

bbits=ST7538_BAUD_600;

break;

case B1200:

baud=1200;

bbits=ST7538_BAUD_1200;

break;

case B1800:

case B2400:

baud=2400;

bbits=ST7538_BAUD_2400;

break;

case B4800:

default:

baud=4800;

bbits=ST7538_BAUD_4800;

break;

}

readRegister(lpmodem,&reg);

reg &= ~ST7538_BAUD;

reg |= bbits;

writeRegister(lpmodem,&reg);

}

else baud=lpmodem->dflt_baud;

#ifdef LPMODEM_DEBUG

printk(KERN_INFO "lpmodem: baud rate: %d\n",baud);

#endif

}

static void lpmodem_deallocate_resources(struct lpmodem_state *lpmodem)

{

DEBUGPLCF("%s\n",__FUNCTION__);

if(lpmodem == NULL) return;

/* remove arbitration timer */

Page 87: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

85

if(lpmodem->modem.flags & ARB_TIMER_ON) del_timer(&lpmodem->arb_timer);

lpmodem->modem.flags&=~ARB_TIMER_ON;

}

static int lpmodem_on_open(struct lpmodem_state *lpmodem)

{

int error;

DEBUGPLCF("%s\n",__FUNCTION__);

if(lpmodem==NULL) return -ENXIO;

DEBUGPLC1("Hardware Start");

#ifndef NOPART

/* For now the parport is my */

if((error=parport_claim_or_block(lpmodem->dev))) return error;

#endif

/* Start Rx mode */

writeMCR(lpmodem,MCR_WD_ | MCR_RXTX);

/* For now default */

lpmodem_set_baud(lpmodem);

/* start arbitration */

if(!(lpmodem->modem.flags & ARB_TIMER_ON))

{

unsigned long ms=lpmodem->ch_params.slottime*10;

lpmodem->modem.flags|=ARB_TIMER_ON;

lpmodem->arb_timer.expires=jiffies+(ms*HZ+999)/1000+1;

add_timer(&lpmodem->arb_timer);

}

return 0;

}

static void lpmodem_on_close(struct lpmodem_state *lpmodem)

{

DEBUGPLCF("%s\n",__FUNCTION__);

if(lpmodem == NULL) return;

/* remove arbitration timer */

if(lpmodem->modem.flags & ARB_TIMER_ON) del_timer(&lpmodem->arb_timer);

lpmodem->modem.flags&=~ARB_TIMER_ON;

writeMCR(lpmodem,MCR_WD_ | MCR_RXTX);

#ifndef NOPART

parport_release(lpmodem->dev);

#endif

}

static int lpmodem_set_hardware(struct lpmodem_state *lpmodem,struct parport *port,int baud)

{

int i;

DEBUGPLCF("%s\n",__FUNCTION__);

if(lpmodem == NULL) return -EINVAL;

lpmodem_deallocate_resources(lpmodem);

lpmodem->parport=port->number;

lpmodem->dflt_baud=baud;

i=0;

if(lpmodem->opened > 0) i=lpmodem_on_open(lpmodem);

return i;

}

/*******************************************************************************

TTY Driver functions

*******************************************************************************/

static inline int lpmodem_paranoia_check(struct lpmodem_state *lpmodem,const char *routine)

{

DEBUGPLCF("%s\n",__FUNCTION__);

if(!lpmodem || lpmodem->magic != LPMODEM_MAGIC)

{

printk(KERN_ERR "lpmodem: bad magic number for lpmodem struct in routine %s\n",routine);

return 1;

}

return 0;

}

/*

* This routine is called when a particular tty device is opened.

* This routine is mandatory; if this routine is not filled in,

* the attempted open will fail with ENODEV.

*/

int lpmodem_open(struct tty_struct *tty,struct file *filp)

Page 88: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

86

{

int line;

struct lpmodem_state *lpmodem;

int i;

st7538reg reg;

unsigned char data;

DEBUGPLCF("%s\n",__FUNCTION__);

DEBUGPLC1("Driver lpmodem is open\n");

if(!tty) return -ENODEV;

line=tty->index - tty->driver->minor_start;

if(line < 0 || line >= NR_PORTS) return -ENODEV;

lpmodem=lpmodem_state+line;

if(lpmodem->opened > 0)

{

lpmodem->opened++;

return 0;

}

/* allocate the buffer space */

if(lpmodem->rx_buf.buffer) kfree(lpmodem->rx_buf.buffer);

if(lpmodem->tx_buf.buffer) kfree(lpmodem->tx_buf.buffer);

lpmodem->rx_buf.buflen=BUFLEN_RX;

lpmodem->tx_buf.buflen=BUFLEN_TX;

lpmodem->rx_buf.rd=lpmodem->rx_buf.wr=0;

lpmodem->tx_buf.rd=lpmodem->tx_buf.wr=0;

lpmodem->rx_buf.buffer=(unsigned char *)kmalloc(lpmodem->rx_buf.buflen,GFP_KERNEL);

lpmodem->tx_buf.buffer=(unsigned char *)kmalloc(lpmodem->tx_buf.buflen,GFP_KERNEL);

if(!lpmodem->rx_buf.buffer || !lpmodem->tx_buf.buffer)

{

if(lpmodem->rx_buf.buffer) kfree(lpmodem->rx_buf.buffer);

if(lpmodem->tx_buf.buffer) kfree(lpmodem->tx_buf.buffer);

lpmodem->rx_buf.buffer=lpmodem->tx_buf.buffer=NULL;

lpmodem->rx_buf.buflen=lpmodem->tx_buf.buflen=0;

return -ENOMEM;

}

i=0; /* enable to open a unitialized port */

i=lpmodem_on_open(lpmodem); /* initialize port */

if((i=readRegister(lpmodem, &reg))) return i;

reg &= ~( ST7538_DET | ST7538_WATCHDOG | ST7538_ASYNC | ST7538_BAUD_4800);

reg |= ST7538_DET_CDC | ST7538_BAUD_4800;

if((i=writeRegister(lpmodem, &reg))) return i;

if(i) return i;

lpmodem->opened++;

tty->driver_data=lpmodem;

lpmodem->tty=tty;

if( tty == NULL){

return -1;

}

// enable_irq(PLC_IRQ);

DEBUGPLC2("ENABLE PLC_IRQ\n");

data = parport_read_control(lpmodem->dev->port);

data |= PLC_IRQ_ENABLE;

parport_write_control(lpmodem->dev->port,data);

return 0;

}

/*

* This routine is called when a particular tty device is closed.

*/

static void lpmodem_close(struct tty_struct *tty,struct file * filp)

{

struct lpmodem_state *lpmodem;

plcMCR data;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!tty) return;

if(lpmodem_paranoia_check(lpmodem=(struct lpmodem_state *)tty->driver_data,"close")) return;

lpmodem->opened--;

if(lpmodem->opened <= 0)

{

lpmodem_on_close(lpmodem); /* clear port */

Page 89: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

87

tty->driver_data=NULL;

lpmodem->tty=NULL;

lpmodem->opened=0;

/* release buffers */

lpmodem->rx_buf.rd=lpmodem->rx_buf.wr=0;

lpmodem->tx_buf.rd=lpmodem->tx_buf.wr=0;

if(lpmodem->rx_buf.buffer) kfree(lpmodem->rx_buf.buffer);

if(lpmodem->tx_buf.buffer) kfree(lpmodem->tx_buf.buffer);

lpmodem->rx_buf.buffer=lpmodem->tx_buf.buffer=NULL;

lpmodem->rx_buf.buflen=lpmodem->tx_buf.buflen=0;

// disable_irq(PLC_IRQ);

DEBUGPLC2("DISABLE PLC_IRQ\n");

data = parport_read_control(lpmodem->dev->port);

data &= ~PLC_IRQ_ENABLE;

parport_write_control(lpmodem->dev->port,data);

}

}

/*

* This routine is called by the kernel to write a single

* character to the tty device. If the kernel uses this routine,

* it must call the flush_chars() routine (if defined) when it is

* done stuffing characters into the driver. If there is no room

* in the queue, the character is ignored.

*/

static void lpmodem_put_char(struct tty_struct *tty,unsigned char ch)

{

struct lpmodem_state *lpmodem;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!tty) return;

if(lpmodem_paranoia_check(lpmodem=(struct lpmodem_state *)tty->driver_data,"put_char")) return;

DEBUGPLC2("put char\n");

if(ch==KISS_FEND)

{

lpmodem_put_fend(lpmodem);

lpmodem->kiss_decode.wr=0;

lpmodem->kiss_decode.escaped=0;

lpmodem->kiss_decode.dec_state=1;

return;

}

if(!lpmodem->kiss_decode.dec_state) return;

if(ch==KISS_FESC)

{

lpmodem->kiss_decode.escaped=1;

return;

}

if(lpmodem->kiss_decode.wr >= sizeof(lpmodem->kiss_decode.pkt_buf))

{

lpmodem->kiss_decode.wr=0;

lpmodem->kiss_decode.dec_state=0;

return;

}

if(lpmodem->kiss_decode.escaped)

{

if(ch==KISS_TFEND) lpmodem->kiss_decode.pkt_buf[lpmodem->kiss_decode.wr++]=KISS_FEND;

else if(ch==KISS_TFESC) lpmodem->kiss_decode.pkt_buf[lpmodem->kiss_decode.wr++]=KISS_FESC;

else

{

lpmodem->kiss_decode.wr=0;

lpmodem->kiss_decode.dec_state=0;

}

lpmodem->kiss_decode.escaped=0;

return;

}

lpmodem->kiss_decode.pkt_buf[lpmodem->kiss_decode.wr++]=ch;

}

/*

* This routine is called by the kernel to write a series of

* characters to the tty device. This routine will return the

* number of characters actually accepted for writing. This

* routine is mandatory.

*/

static int lpmodem_write(struct tty_struct *tty,const unsigned char *buf,int count)

{

struct lpmodem_state *lpmodem;

const unsigned char *bp;

int c;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!tty || !buf || count <= 0) return count;

if(lpmodem_paranoia_check(lpmodem=(struct lpmodem_state *)tty->driver_data,"write")) return count;

for(c=count,bp=buf;c > 0;c--,bp++) lpmodem_put_char(tty,*bp);

if((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty);

wake_up_interruptible(&tty->write_wait);

Page 90: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

88

return count;

}

/*

* This routine returns the numbers of characters the tty driver

* will accept for queuing to be written. This number is subject

* to change as output buffers get emptied, or if the output flow

* control is acted.

*/

static int lpmodem_write_room(struct tty_struct *tty)

{

struct lpmodem_state *lpmodem;

int free;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!tty) return 0;

if(lpmodem_paranoia_check(lpmodem=(struct lpmodem_state *)tty->driver_data,"write_room")) return 0;

free=lpmodem->tx_buf.rd-lpmodem->tx_buf.wr;

if(free <= 0)

{

free=lpmodem->tx_buf.buflen-lpmodem->tx_buf.wr;

if((unsigned int)free < lpmodem->tx_buf.rd) free=lpmodem->tx_buf.rd; /* we may fold */

}

return free / 2; /* a rather pessimistic estimate */

}

/*

* This routine returns the numbers of characters the tty driver

* has in queue to be written to tty device. This number is subject

* to change as output buffers get emptied.

*/

static int lpmodem_chars_in_buffer(struct tty_struct *tty)

{

struct lpmodem_state *lpmodem;

int cnt;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!tty) return 0;

if(lpmodem_paranoia_check(lpmodem=(struct lpmodem_state *)tty->driver_data,"chars_in_buffer")) return 0;

cnt=lpmodem->tx_buf.wr-lpmodem->tx_buf.rd;

if(cnt < 0) cnt+=lpmodem->tx_buf.buflen;

return cnt;

}

/*

* This routine is called by the kernel after it has written a

* series of characters to the tty device using write().

*/

static void lpmodem_flush_buffer(struct tty_struct *tty)

{

struct lpmodem_state *lpmodem;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!tty) return;

if(lpmodem_paranoia_check(lpmodem=(struct lpmodem_state *)tty->driver_data,"flush_buffer")) return;

wake_up_interruptible(&tty->write_wait);

if((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty);

}

/*

* This routine allows the tty driver to implement

* device-specific ioctl’s. If the ioctl number passed in cmd

* is not recognized by the driver, it should return ENOIOCTLCMD.

*/

static int lpmodem_ioctl(struct tty_struct *tty,struct file * file,unsigned int cmd,unsigned long arg)

{

struct lpmodem_state *lpmodem;

struct lpmodem_params par;

int i;

int baud;

int err;

DEBUGPLCF("%s\n",__FUNCTION__);

if (!tty) return -EINVAL;

if(lpmodem_paranoia_check(lpmodem=(struct lpmodem_state *)tty->driver_data,"ioctl")) return -EINVAL;

// printk("%x", cmd);

switch(cmd)

{

default:

{

return -ENOIOCTLCMD;

}

case TIOCMGET:

{

i=access_ok(VERIFY_WRITE,(void *)arg,sizeof(int));

if(!i) return -EFAULT;

i=(lpmodem->modem.dcd ? TIOCM_CAR : 0) | (lpmodem->hdlc_tx.ptt ? TIOCM_RTS : 0);

put_user(i,(int *)arg);

return 0;

}

Page 91: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

89

case LPMODEM_GET_DCD:

{

int i=access_ok(VERIFY_WRITE,(void *)arg,sizeof(unsigned char));

if(!i) return -EFAULT;

put_user(lpmodem->modem.dcd,(unsigned char *) arg);

return 0;

}

case LPMODEM_GET_PTT:

{

i=access_ok(VERIFY_WRITE,(void *)arg,sizeof(unsigned char));

if(!i) return -EFAULT;

put_user(lpmodem->hdlc_tx.ptt,(unsigned char *)arg);

return 0;

}

case LPMODEM_SET_TXDELAY:

{

if(arg > 255) return -EINVAL;

lpmodem->ch_params.tx_delay=arg;

return 0;

}

case LPMODEM_SET_PPERSIST:

{

if(arg > 255) return -EINVAL;

lpmodem->ch_params.ppersist=arg;

return 0;

}

case LPMODEM_SET_SLOTTIME:

{

if (arg > 255) return -EINVAL;

lpmodem->ch_params.slottime=arg;

return 0;

}

case LPMODEM_SET_TXTAIL:

{

if(arg > 255) return -EINVAL;

lpmodem->ch_params.tx_tail=arg;

return 0;

}

case LPMODEM_SET_FULLDUP:

{

lpmodem->ch_params.fulldup=arg ? 1 : 0;

return 0;

}

case LPMODEM_GET_PARAMS:

{

i = access_ok(VERIFY_WRITE,(void *) arg,sizeof(par));

if(!i) return -EFAULT;

par.parport=lpmodem->parport;

par.baud=lpmodem->dflt_baud;

par.tx_delay=lpmodem->ch_params.tx_delay;

par.tx_tail=lpmodem->ch_params.tx_tail;

par.slottime=lpmodem->ch_params.slottime;

par.ppersist=lpmodem->ch_params.ppersist;

par.fulldup=lpmodem->ch_params.fulldup;

if((err=copy_to_user((void *)arg,&par,sizeof(par)))) return err;

return 0;

}

case LPMODEM_SET_PARAMS:

{

if(!capable(CAP_SYS_ADMIN)) return -EPERM;

i=access_ok(VERIFY_READ,(void *) arg,sizeof(par));

if(!i) return -EFAULT;

if((err=copy_from_user(&par,(void *)arg,sizeof(par)))) return err;

i=lpmodem_set_hardware(lpmodem,lpmodem->dev->port,par.baud);

if(i) return i;

/* set termios baud flag to default baud */

baud=0;

while((baud_table[baud] > 0 || baud == 0) && (baud_table[baud] != lpmodem->dflt_baud)) baud++;

if(baud > 15) baud |= CBAUDEX;

tty->termios->c_cflag&=~CBAUD;

tty->termios->c_cflag|=baud;

lpmodem->ch_params.tx_delay=par.tx_delay;

lpmodem->ch_params.tx_tail=par.tx_tail;

lpmodem->ch_params.slottime=par.slottime;

lpmodem->ch_params.ppersist=par.ppersist;

lpmodem->ch_params.fulldup=par.fulldup;

return 0;

}

case LPMODEM_GET_STAT:

{

i=access_ok(VERIFY_WRITE,(void *) arg,sizeof(struct lpmodem_statistics));

if(!i) return -EFAULT;

if((err=copy_to_user((void *)arg,&lpmodem->stat,sizeof(struct lpmodem_statistics)))) return err;

return 0;

Page 92: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

90

}

#ifdef LPMODEM_DEBUG

case LPMODEM_GET_BITS:

{

if(lpmodem->bitbuf_hdlc.rd == lpmodem->bitbuf_hdlc.wr) return -EAGAIN;

i=access_ok(VERIFY_WRITE,(void *) arg,sizeof(unsigned char));

if(!i) return -EFAULT;

put_user(lpmodem->bitbuf_hdlc.buffer[lpmodem->bitbuf_hdlc.rd],(unsigned char *) arg);

lpmodem->bitbuf_hdlc.rd=(lpmodem->bitbuf_hdlc.rd+1) % sizeof(lpmodem->bitbuf_hdlc.buffer);

return 0;

}

case LPMODEM_DEBUG1:

{

i=access_ok(VERIFY_WRITE,(void *) arg,sizeof(unsigned long));

if(!i) return -EFAULT;

put_user((lpmodem->rx_buf.wr-lpmodem->rx_buf.rd) % lpmodem->rx_buf.buflen,(unsigned long *)arg);

return 0;

}

case LPMODEM_DEBUG2:

{

i=access_ok(VERIFY_WRITE,(void *) arg,sizeof(unsigned long));

if(!i) return -EFAULT;

put_user(lpmodem->debug_vals.last_intcnt,(unsigned long *)arg);

return 0;

}

case LPMODEM_GET_MSR:

{

plcMSR msr;

i=access_ok(VERIFY_WRITE,(void *) arg,sizeof(plcMSR));

if(!i) return -EFAULT;

if((i=readMSR(lpmodem,&msr))) return i;

put_user(msr,(plcMSR *)arg);

return 0;

}

case LPMODEM_GET_REG:

{

st7538reg reg, reg1;

i=access_ok(VERIFY_WRITE,(void *) arg,sizeof(st7538reg));

if(!i) return -EFAULT;

do {

schedule();

schedule();

if((i=readRegister(lpmodem,&reg))) return i;

/* This modem is lazy, if 0x0 give it a time */

msleep(100);

schedule();

schedule();

if((i=readRegister(lpmodem,&reg1))) return i;

msleep(100);

/* Ok, is really this? */

}while(reg != reg1);

put_user(reg,(st7538reg *)arg);

return 0;

}

case LPMODEM_SET_REG:

{

st7538reg reg=arg;

if((i=writeRegister(lpmodem, &reg))) return i;

return 0;

}

#endif /* LPMODEM_DEBUG */

}

return 0;

}

/*

* This routine allows the tty driver to be notified when

* device’s termios settings have changed. Note that a

* well-designed tty driver should be prepared to accept the case

* where old == NULL, and try to do something rational.

*/

static void lpmodem_set_termios(struct tty_struct *tty,struct termios *old)

{

struct lpmodem_state *lpmodem;

DEBUGPLCF("%s\n",__FUNCTION__);

if(!tty) return;

if(lpmodem_paranoia_check(lpmodem=(struct lpmodem_state *)tty->driver_data,"set_termios")) return;

Page 93: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

91

if(tty->termios->c_cflag == old->c_cflag) return;

lpmodem_set_baud(lpmodem);

}

/*******************************************************************************

Driver initialization & cleanup functions

*******************************************************************************/

static void init_channel(struct lpmodem_state *lpmodem,int port)

{

DEBUGPLCF("%s\n",__FUNCTION__);

if(!lpmodem) return;

/* HDLC initialization */

rx_bit_buff=0;

tx_bit_buff=0;

rxcont=0;

txcont=0;

plc_fixme2 = 1;

lpmodem->hdlc_rx.rx_state=0;

lpmodem->hdlc_tx.tx_state=0;

lpmodem->hdlc_tx.numflags=0;

lpmodem->hdlc_tx.bitstream=0;

lpmodem->hdlc_tx.ptt=0;

lpmodem->hdlc_tx.holdptt=0;

lpmodem->hdlc_rx.bh_rd=0;

lpmodem->hdlc_rx.bh_wr=0;

lpmodem->hdlc_rx.bh_buflen=0;

lpmodem->hdlc_tx.bh_rd=0;

lpmodem->hdlc_tx.bh_wr=0;

lpmodem->hdlc_tx.bh_buflen=0;

#ifdef LPMODEM_DEBUG

lpmodem->bitbuf_hdlc.rd=0;

lpmodem->bitbuf_hdlc.wr=0;

#endif /* LPMODEM_DEBUG */

/* KISS initialization */

lpmodem->kiss_decode.dec_state=0;

lpmodem->kiss_decode.escaped=0;

lpmodem->kiss_decode.wr=0;

lpmodem->ch_params=lpmodem_ports[port].dflt_ch_params;

/* Arbitration initialization */

lpmodem->modem.dcd=0;

init_timer(&lpmodem->arb_timer);

lpmodem->arb_timer.function=arb_sched;

lpmodem->arb_timer.data=(unsigned long) lpmodem;

lpmodem->modem.flags=0;

/* Bottom-half initialization */

#ifdef LPMODEM_USE_BH

INIT_WORK(&(lpmodem->tq_receiver),bh_receiver,lpmodem);

INIT_WORK(&(lpmodem->tq_transmitter),bh_transmitter,lpmodem);

#endif /* LPMODEM_USE_BH */

}

static void init_datastructs(void)

{

int i;

DEBUGPLCF("%s\n",__FUNCTION__);

for(i=0;i < NR_PORTS;i++)

{

struct lpmodem_state *lpmodem=lpmodem_state+i;

lpmodem->magic=LPMODEM_MAGIC;

lpmodem->parport=0xffffffff;

lpmodem->opened=0;

lpmodem->tty=NULL;

lpmodem->rx_buf.rd=0;

lpmodem->rx_buf.wr=0;

lpmodem->rx_buf.buflen=0;

lpmodem->rx_buf.buffer=NULL;

lpmodem->tx_buf.rd=0;

lpmodem->tx_buf.wr=0;

lpmodem->tx_buf.buflen=0;

lpmodem->tx_buf.buffer=NULL;

memset(&lpmodem->stat,0,sizeof(lpmodem->stat));

Page 94: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

92

init_channel(lpmodem,i);

}

}

#ifndef NOPART

static void lpmodem_attach(struct parport *port)

{

unsigned int i;

for(i=0;i < NR_PORTS;i++)

{

struct lpmodem_state *lpmodem=lpmodem_state+i;

if (port->number == lpmodem_ports[i].parport)

{

lpmodem->dev = parport_register_device(port,"lpmodem",NULL,NULL,NULL,0,(void *) lpmodem);

printk(/*KERN_INFO */"ttyPLC%d: using %s.\n",port->number,port->name);

lpmodem_set_hardware(lpmodem,port,lpmodem_ports[i].baud);

}

}

}

static void lpmodem_detach(struct parport *port)

{

}

static struct parport_driver pp_driver=

{

.name = "lpmodem",

.attach = lpmodem_attach,

.detach = lpmodem_detach,

};

#endif

static int lpmodem_init(void)

{

int baud;

int i;

struct lpmodem_state *lpmodem=lpmodem_state;

DEBUGPLCF("%s\n",__FUNCTION__);

/* initialize the data structures */

init_datastructs();

/* register the driver as tty driver */

memset(&lpmodem_driver,0, sizeof(struct tty_driver));

lpmodem_driver.magic=TTY_DRIVER_MAGIC;

lpmodem_driver.name="lpmodem";

lpmodem_driver.major=major;

lpmodem_driver.minor_start=0;

lpmodem_driver.num=NR_PORTS;

lpmodem_driver.type=TTY_DRIVER_TYPE_LPMODEM;

lpmodem_driver.subtype=LPMODEM_TYPE_NORMAL;

lpmodem_driver.init_termios.c_iflag=0;

lpmodem_driver.init_termios.c_oflag=0;

baud=0;

while((baud_table[baud] > 0 || baud == 0) && (baud_table[baud] != DFLT_BAUD)) baud++;

if(baud > 15) baud |= CBAUDEX;

lpmodem_driver.init_termios.c_cflag=CS8 | CREAD | CLOCAL | baud;

lpmodem_driver.init_termios.c_lflag=0;

lpmodem_driver.flags=TTY_DRIVER_REAL_RAW;

lpmodem_driver.refcount=lpmodem_refcount;

lpmodem_driver.ttys=lpmodem_ttys;

lpmodem_driver.termios=lpmodem_termios;

lpmodem_driver.termios_locked=lpmodem_termios_locked;

/* the functions */

lpmodem_driver.open=lpmodem_open;

lpmodem_driver.close=lpmodem_close;

lpmodem_driver.write= lpmodem_write;

lpmodem_driver.put_char=lpmodem_put_char;

lpmodem_driver.flush_chars=NULL;

lpmodem_driver.write_room=lpmodem_write_room;

lpmodem_driver.chars_in_buffer=lpmodem_chars_in_buffer;

lpmodem_driver.flush_buffer=lpmodem_flush_buffer;

lpmodem_driver.ioctl=lpmodem_ioctl;

/* cannot throttle the transmitter on this layer */

lpmodem_driver.throttle=NULL;

lpmodem_driver.unthrottle=NULL;

/* no special actions on termio changes */

lpmodem_driver.set_termios=lpmodem_set_termios;

/* no XON/XOFF and no hangup on the radio port */

lpmodem_driver.stop=NULL;

lpmodem_driver.start=NULL;

lpmodem_driver.hangup=NULL;

lpmodem_driver.set_ldisc=NULL;

if(tty_register_driver(&lpmodem_driver))

{

printk(KERN_ERR "lpmodem: tty_register_driver() failed.\n");

return -EIO;

Page 95: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

93

}

#ifndef NOPART

if(parport_register_driver(&pp_driver))

{

printk (KERN_ERR "lpmodem: unable to register with parport.\n");

if(tty_unregister_driver(&lpmodem_driver)) printk(KERN_ERR "lpmodem: failed to unregister tty driver.\n");

return -EIO;

}

#endif

if ((i = request_irq(PLC_IRQ, lpmodem_interrupt, 0, "lpmodem", lpmodem))) return i;

return 0;

}

int init_module(void)

{

int i;

printk("lpmodem: v2.0.0 Copyright (c) 2005 \tWalter Fetter Lages <[email protected]>.\n"\

"\t\t\t\t\tDiego Caberlon Santini <[email protected]>\n");

printk(KERN_INFO "lpmodem: init_module() called.\n");

for(i=0; i < NR_PORTS; i++)

{

lpmodem_ports[i].parport=parport[i];

lpmodem_ports[i].baud=defbaud[i];

lpmodem_ports[i].dflt_ch_params.tx_delay=tx_delay[i];

lpmodem_ports[i].dflt_ch_params.tx_tail=tx_tail[i];

lpmodem_ports[i].dflt_ch_params.slottime=slottime[i];

lpmodem_ports[i].dflt_ch_params.ppersist=ppersist[i];

lpmodem_ports[i].dflt_ch_params.fulldup=fulldup[i];

}

/*

* ANSI sample implementation borrowed form Metaware High C Reference Manual

* ANSI uses 1 as seed. Here seed is get from jiffies to be more, well, random.

*/

rand_seed=(unsigned short) jiffies; /* random seed */

return lpmodem_init();

}

void cleanup_module(void)

{

int i;

struct lpmodem_state *lpmodem;

printk(KERN_INFO "lpmodem: cleanup_module() called.\n");

lpmodem=lpmodem_state;

free_irq(PLC_IRQ, lpmodem);

#ifndef NOPART

if(tty_unregister_driver(&lpmodem_driver)) printk(KERN_WARNING "lpmodem: failed to unregister tty driver.\n");

#endif

for(i=0;i < NR_PORTS;i++)

{

lpmodem=lpmodem_state+i;

if(lpmodem->magic != LPMODEM_MAGIC) printk(KERN_ERR "lpmodem: invalid magic in cleanup_module.\n");

else

{

if(lpmodem->dev != NULL) parport_unregister_device(lpmodem->dev);

parport_unregister_driver(&pp_driver);

lpmodem_deallocate_resources(lpmodem);

/* free the buffers */

lpmodem->rx_buf.rd=0;

lpmodem->rx_buf.wr=0;

lpmodem->tx_buf.rd=0;

lpmodem->tx_buf.wr=0;

if(lpmodem->rx_buf.buffer) kfree(lpmodem->rx_buf.buffer);

if(lpmodem->tx_buf.buffer) kfree(lpmodem->tx_buf.buffer);

lpmodem->rx_buf.buffer=NULL;

lpmodem->tx_buf.buffer=NULL;

lpmodem->rx_buf.buflen=0;

lpmodem->tx_buf.buflen=0;

}

}

}

Page 96: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

94

Page 97: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

95

APENDICE D HEADER DA PLACA, LPMODEM.H

/*****************************************************************************

lpmodem PLC Modem Driver

2005 Walter Fetter Lages <[email protected]>

Diego Caberlon Santini <[email protected]>

This program is free software; you can redistribute it and/or modify

it under the terms of the GNU General Public License as published by

the Free Software Foundation; either version 2 of the License, or

(at your option) any later version.

This program is distributed in the hope that it will be useful,

but WITHOUT ANY WARRANTY; without even the implied warranty of

MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

GNU General Public License for more details.

You should have received a copy of the GNU General Public License

along with this program; if not, write to the Free Software

Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Please note that the GPL allows you to use the driver, NOT the radio.

In order to use the radio, you need a license from the communications

authority of your country.

History:

2006.06.19 Start development based on BIM 3.0.0

*****************************************************************************/

#ifndef _LPMODEM_H

#define _LPMODEM_H

/** @file lpmodem.h lpmodem driver header.

*/

#include <linux/ioctl.h>

#include <st7538.h>

/** Plcmodem Modem Status Register bits

*/

enum PLC_MSR_BITS

{

PLC_MSR_CLRT =0x404048L,/**<CLRT */

PLC_MSR_RXD =0x000010L,/**<RXD */

PLC_MSR_RESET_ =0x000000L,/**<RESET# */

PLC_MSR_MCLK =0x000080L,/**<MCLK */

PLC_MSR_TOUT =0x000800L,/**<TOUT */

PLC_MSR_REGOK_ =0x001000L,/**<REG_OK# */

PLC_MSR_PG =0x008000L,/**<PG */

PLC_MSR_ZCOUT =0x080000L,/**<ZCOUT */

PLC_MSR_BU =0x100000L,/**<BU */

PLC_MSR_CD_PD =0x800000L /**<CD/PD */

};

/** Plcmodem Modem Control Register bits

*/

enum MCR_BITS

{

MCR_REG_DATA =0x01,/**<REG/DATA */

MCR_TXD =0x02,/**<TXD */

MCR_RXTX =0x04,/**<RxTx */

MCR_WD_ =0x08 /**<WD# */

};

typedef int plcError; /**< Error code */

typedef void *plcHandle; /**< Handle for a PLC device */

typedef unsigned long plcMSR; /**< plcmodem MSR */

typedef unsigned char plcMCR; /**< plcmodem MCR */

/** lpmodem driver statistics.

*/

struct lpmodem_statistics

{

unsigned long rx_packets; /**< Received packets. */

Page 98: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

96

unsigned long tx_packets; /**< Transmitted packets. */

unsigned long ptt_keyed; /**< Number of times the ptt was keyed */

unsigned long rx_bufferoverrun; /**< Number of RX buffer overruns */

unsigned long tx_bufferoverrun; /**< Number of TX buffer overruns */

};

/** lpmodem KISS parameters.

*/

struct lpmodem_params

{

int parport; /**< Parport number to use. */

int baud; /**< Default baud rate */

int tx_delay; /**< Transmitter keyup delay in 10ms units. */

int tx_tail; /**< Transmitter keyoff delay in 10ms units. */

int slottime; /**< Slottime in 10ms; usually 10 = 100ms. */

int ppersist; /**< p-persistence 0..255. */

int fulldup; /**< Driver does not support full duplex, setting this just makes the driver send even if DCD is on. */

};

/** Maximum packet length, excluding CRC.

*/

#define LPMODEM_MAXFLEN 400

/** KISS protocol special characters.

*/

enum KISS_CHARS

{

KISS_FEND =0xc0, /**< Frame End. */

KISS_FESC =0xdb, /**< Frame Escape. */

KISS_TFEND =0xdc, /**< Transposed Frame End. */

KISS_TFESC =0xdd /**< Transpoded Frame Escape. */

};

/** KISS protocol commands.

*/

enum KISS_CMD

{

KISS_CMD_DATA =0, /**< The rest of the frame is data to be sent on the HDLC channel. */

KISS_CMD_TXDELAY =1, /**< The next byte is the transmitter keyup delay in 10 ms units. */

KISS_CMD_PPERSIST =2, /**< The next byte is the persistence parameter, p, scaled to the range 0 - 255. */

KISS_CMD_SLOTTIME =3, /**< The next byte is the slot interval in 10 ms units. */

KISS_CMD_TXTAIL =4, /**< The next byte is the time to hold up the TX after the FCS has been sent, in 10 ms units. */

KISS_CMD_FULLDUP =5, /**< The next byte is 0 for half duplex, nonzero for full duplex. */

KISS_CMD_SETHW =6, /**< Specific for each TNC. */

};

/** lpmodem ioctl commands.

*/

enum LPMODEM_CTL

{

LPMODEM_GET_DCD =_IOR(’L’,0,unsigned char), /**< Get the DCD state. */

LPMODEM_GET_PTT =_IOR(’L’,1,unsigned char), /**< Get the ptt state. */

LPMODEM_SET_TXDELAY =_IOW(’L’,2,unsigned char), /**< Set the transmitter keyup delay in 10 ms units. */

LPMODEM_SET_PPERSIST =_IOW(’L’,3,unsigned char), /**< Set the p-persistence scaled to the range 0-255. */

LPMODEM_SET_SLOTTIME =_IOW(’L’,4,unsigned char), /**< Set the slot interval in 10 ms units. */

LPMODEM_SET_TXTAIL =_IOW(’L’,5,unsigned char), /**< Set the time to hold up the TX after the FCS has been sent, in 10 ms units. */

LPMODEM_SET_FULLDUP =_IOW(’L’,6,unsigned char), /**< Set to zero for half duplex, nonzero for full duplex. */

LPMODEM_GET_STAT =_IOR(’L’,7,struct lpmodem_statistics), /**< Get lpmodem statistics. */

LPMODEM_GET_PARAMS =_IOR(’L’,8,struct lpmodem_params), /**< Set KISS lpmodem parameters. */

LPMODEM_SET_PARAMS =_IOR(’L’,9,struct lpmodem_params), /**< Get KISS lpmodem parameters. */

LPMODEM_DEBUG_TEST =_IOR(’L’,31,st7538reg), /**< Set the transmitter keyup delay in 10 ms units. */

#ifdef LPMODEM_DEBUG

LPMODEM_GET_BITS =_IOR(’L’,17,unsigned char), /**< Get raw HDLC frame. */

LPMODEM_DEBUG1 =_IOR(’L’,18,unsigned long), /**< Get the free space in RX buffer */

LPMODEM_DEBUG2 =_IOR(’L’,19,unsigned long), /**< Get the frenquency of interrupts */

#endif /* LPMODEM_DEBUG */

LPMODEM_GET_MSR =_IOR(’L’,20,plcMSR), /**< Get MSR register. */

LPMODEM_GET_REG =_IOR(’L’,21,st7538reg), /**< Get ST7538 Register register. */

LPMODEM_SET_REG =_IOR(’L’,22,st7538reg), /**< Set ST7538 Register register. */

};

#endif /* _LPMODEM_H */

Page 99: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

97

APENDICE E HEADER DO ST7538, ST7538.H

#ifndef _ST7538_H

#define _ST7538_H

/** @file st7538.h ST7538 definitions header.

*/

/** ST7538 Control Register Bits.

*/

enum ST7538_CTRL_BITS

{

ST7538_FREQ =0x000007L, /**<channel frequency mask */

ST7538_BAUD =0x000018L, /**<baud rate mask */

ST7538_DEV =0x000020L, /**<select deviation of 1 */

ST7538_WATCHDOG =0x000040L, /**<enable watchdog */

ST7538_TIMEOUT =0x000180L, /**<time out mask */

ST7538_DET_TIME =0x000600L, /**<frequency detection time mask */

ST7538_ZEROSYNC =0x000800L, /**<enable zero crossing synchronization */

ST7538_DET =0x003000L, /**<detection method mask */

ST7538_ASYNC =0x004000L, /**<select asynchronous interfacing mode*/

ST7538_CLK =0x018000L, /**<output clock mask */

ST7538_PACKBAUD =0x060000L, /**<packet mode baud rate mask */

ST7538_PACKLEN =0x180000L, /**<packet lenght mask */

ST7538_PACK =0x200000L, /**<enable packet mode */

ST7538_HISENSE =0x400000L, /**<enable high sensitivity mode */

ST7538_FILTER =0x800000L /**<enable input filter */

};

/** ST7538 Channel Frequencies.

*/

enum ST7538_FREQ_BITS

{

ST7538_FREQ_60 =0x000000L, /**<select 60kHz channel frequency */

ST7538_FREQ_66 =0x000001L, /**<select 60kHz channel frequency */

ST7538_FREQ_72 =0x000002L, /**<select 72kHz channel frequency */

ST7538_FREQ_76 =0x000003L, /**<select 76kHz channel frequency */

ST7538_FREQ_82 =0x000004L, /**<select 82.05kHz channel frequency */

ST7538_FREQ_86 =0x000005L, /**<select 86kHz channel frequency */

ST7538_FREQ_110 =0x000006L, /**<select 110kHz channel frequency */

ST7538_FREQ_132 =0x000007L /**<select 132.5kHz channel frequency */

};

/** ST7538 Baud Rates.

*/

enum ST7538_BAUD_BITS

{

ST7538_BAUD_600 =0x000000L, /**<select 600bps rate */

ST7538_BAUD_1200=0x000008L, /**<select 1200bps rate */

ST7538_BAUD_2400=0x000010L, /**<select 2400bps rate */

ST7538_BAUD_4800=0x000018L /**<select 4800bps rate */

};

/** ST7538 Time Outs.

*/

enum ST7538_TIMEOUT_BITS

{

ST7538_NO_TIMEOUT =0x000000L, /**<no time out */

ST7538_TIMEOUT_1 =0x000080L, /**<1s time out */

ST7538_TIMEOUT_3 =0x000100L /**<3s time out */

};

/** ST7538 Frequency Detection Times.

*/

enum ST7538_DET_TIME_BITS

{

ST7538_DET_TIME_500 =0x000000L, /**<select 500us frequency detection time */

ST7538_DET_TIME_1 =0x000200L, /**<select 1ms frequency detection time */

ST7538_DET_TIME_3 =0x000400L, /**<select 3ms frequency detection time */

ST7538_DET_TIME_5 =0x000600L /**<select 5ms frequency detection time */

};

/** ST7538 Detection Methods.

*/

enum ST7538_DET_BITS

{

ST7538_DET_CD =0x000000L, /**<select carrier detection without conditioning */

Page 100: DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX

98

ST7538_DET_CDC =0x001000L, /**<select carrier detection with conditioning */

ST7538_DET_PD =0x002000L, /**<select preamble detection without conditioning */

ST7538_DET_PDC =0x003000L /**<select preamble detection with conditioning */

};

/** ST7538 Output Clocks.

*/

enum ST7538_CLK_BITS

{

ST7538_CLK_16 =0x000000L, /**<select 16MHz output clock */

ST7538_CLK_8 =0x008000L, /**<select 8MHz output clock */

ST7538_CLK_4 =0x010000L /**<select 4MHz output clock */

};

/** ST7538 Packet Mode Baud Rates.

*/

enum ST7538_PACKBAUD_BITS

{

ST7538_PACKBAUD_32 =0x000000L, /**<select Mclk/32 packet mode baud rate */

ST7538_PACKBAUD_64 =0x020000L, /**<select Mclk/64 packet mode baud rate */

ST7538_PACKBAUD_128 =0x040000L, /**<select Mclk/128 packet mode baud rate */

ST7538_PACKBAUD_256 =0x060000L /**<select Mclk/256 packet mode baud rate */

};

/** ST7538 Packet Lenghts.

*/

enum ST7538_PACKLEN_BITS

{

ST7538_PACKLEN_8 =0x000000L, /**<select 8 bits packet lenght */

ST7538_PACKLEN_9 =0x080000L, /**<select 9 bits packet lenght */

ST7538_PACKLEN_14 =0x100000L, /**<select 14 bits packet lenght */

ST7538_PACKLEN_16 =0x180000L /**<select 16 bits packet lenght */

};

#ifdef __MSDOS__

/* In MS-DOS compilers enum is usually 16 bits */

#define ST7538_PACKLEN_9 0x080000L

#define ST7538_PACKLEN_14 0x100000L

#define ST7538_PACKLEN_16 0x180000L

#endif

typedef unsigned long st7538reg; /**< ST7538 register */

#endif