desenvolvimento de um modem plc em plataforma linux
Transcrição
desenvolvimento de um modem plc em plataforma linux
UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL ESCOLA DE ENGENHARIA DEPARTAMENTO DE ENGENHARIA ELÉTRICA CURSO DE GRADUAÇÃO EM ENGENHARIA ELÉTRICA DIEGO CABERLON SANTINI DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX Porto Alegre 2006 DIEGO CABERLON SANTINI DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX Projeto de Diplomação apresentado ao Departamento de Engenharia Elétrica da Universidade Federal do Rio Grande do Sul como parte dos requisitos para a obtenção do tı́tulo de Engenheiro Eletricista. ORIENTADOR: Prof. Dr. Walter Fetter Lages Porto Alegre 2006 DIEGO CABERLON SANTINI DESENVOLVIMENTO DE UM MODEM PLC EM PLATAFORMA LINUX Este Projeto foi julgado adequado para a obtenção dos créditos da Disciplina Projeto de Diplomação do Departamento de Engenharia Elétrica e aprovado em sua forma final pelo Orientador e pela Banca Examinadora. Orientador: Prof. Dr. Walter Fetter Lages, UFRGS Doutor pela Instituto Tecnológico de Aeronáutica – São José dos Campos, Brasil Banca Examinadora: Prof. Dr. Walter Fetter Lages, UFRGS Doutor pelo Instituto Tecnológico de Aeronáutica – São José dos Campos, Brasil Prof. Dr. Renato Ventura Bayan Henriques, UFRGS Doutor pela Universidade Federal de Minas Gerais – Belo Horizonte, Brasil Prof. Dr. Romeu Reginatto, UFRGS Doutor pela Universidade Federal de Santa Catarina – Florianópolis, Brasil Chefe do DELET: Prof. Dr. Roberto Petry Homrich Porto Alegre, dezembro de 2006. DEDICATÓRIA Dedico esse trabalho para todas as pessoas que diretamente ou indiretamente contribuı́ram para o meu crescimento intelectual ao longo da minha vida acadêmica. AGRADECIMENTOS Primeiramente gostaria de agradecer aos meus pais, Gilberto Santini e Maria Ines Caberlon Santini, por toda a estrutura que me têm oferecido ao longo da minha vida e especialmente pelo o esforço que fizeram para que eu me tornasse um Engenheiro Eletricista. Também agradeço ao meu irmão, Thiago Caberlon Santini, pelo incentivo que meu deu ao longo da minha vida. Ao meu orientador, Walter Fetter Lages, pelos três anos sobre a sua orientação pelo qual muito devo o meu atual conhecimento. Ao meu supervisor, Julio Klemm, pela a oportunidade me dada para consolidar meus conhecimentos durante o estágio. À Universidade Federal do Rio Grande do Sul e aos seus funcionários pela constante infra-estrutura oferecida, e pela a oportunidade de estudar gratuitamente em uma das melhores universidades do Brasil. Por fim, aos meus colegas que me apoiaram durante a faculdade e que, com certeza, não chegaria até aqui sem eles. RESUMO Este trabalho trata dos passos do desenvolvimento de um modem PLC que está sendo construindo no LASCAR, utilizando o chip ST7538. Neste trabalho consta uma breve descrição a respeito da comunicação PLC. A seguir serão apresentadas as caracterı́sticas do hardware que foi desenvolvido, incluindo a parte digital e analógica do modem. Finalmente será explorado a parte de software do mesmo, com uma introdução a sistemas operacionais e o linux, com o desenvolvimento de driver TTY para plataforma Linux-2.6.17 com os protocolos HDLC, KISS, TCP/UDP, IP. Palavras-chave: PLC, modem PLC, Linux, drivers TTY, ST7538. ABSTRACT This work deals with the steps for chip development of a PLC modem that is being built at LASCAR, with ST7538. This work present a description of PLC communication, its advantages and disadvantages. In the sequence, the characteristics of the hardware are described, including digital and analogical components. Finally the software will be explored, with an introduction to operational system and Linux and 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. SUMÁRIO LISTA DE ILUSTRAÇÕES . . . . . . . . . . . . . . . . . . . . . . . . . . 15 LISTA DE TABELAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 LISTA DE ABREVIATURAS . . . . . . . . . . . . . . . . . . . . . . . . . 19 1 INTRODUÇÃO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.1 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.2 Organização do trabalho . . . . . . . . . . . . . . . . . . . . . . . . 22 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 23 23 24 24 26 26 26 3 HARDWARE DO MODEM PLC . . . . 3.1 Parte Analógica . . . . . . . . . . . . 3.1.1 ST7538-PowerLine FSK Transceiver 3.1.2 Componente Analógicos . . . . . . . 3.2 Parte Digital . . . . . . . . . . . . . . 3.2.1 Interface com a Porta Paralela . . . . 3.3 Mudança de Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 29 29 32 32 32 35 4 SOFTWARE DO MODEM PLC . . 4.1 Plataforma de Desenvolvimento 4.1.1 Sistemas Operacionais . . . . . . 4.2 Linguagem C . . . . . . . . . . . 4.2.1 Programando no Kernel do Linux 4.3 Especificações de Hardware . . 4.4 Kernel Space: Driver TTY . . 4.4.1 Funções de Acesso ao Hardware . 4.4.2 Funções de Protocolos . . . . . . 4.4.3 Funções de Driver TTY . . . . . 4.5 User Space: kissattach . . . . . . . . . . . . . . . . . . . . Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 37 38 40 40 42 42 43 44 49 50 2 TECNOLOGIA PLC . . . . . . . . 2.1 Introdução a Tecnologia PLC 2.1.1 X-10 Uma Primeira Idéia . . . . 2.1.2 Problemas da Tecnologia PLC . 2.1.3 Vantagens da Tecnologia PLC . 2.2 Tipos de Redes PLC . . . . . . 2.2.1 Last Mile Access . . . . . . . . 2.2.2 Last Inch Access . . . . . . . . . . . . . . . . . . . . . . . . . . do . . . . . . . . . . . . . . . . . . CONCLUSÃO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 REFERÊNCIAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 APÊNDICE A DIAGRAMAS ESQUEMÁTICOS DO MODEM PLC . . . 59 APÊNDICE B ARTE FINAL DO PCB DO MODEM PLC . . . . . . . . 63 APÊNDICE C CÓDIGO FONTE DO DRIVER DO MODEM PLC . . . 69 APÊNDICE D HEADER DA PLACA, LPMODEM.H . . . . . . . . . . . 95 APÊNDICE E 97 5 HEADER DO ST7538, ST7538.H . . . . . . . . . . . . . LISTA DE ILUSTRAÇÕES Figura Figura Figura Figura Figura 2.1: 2.2: 2.3: 2.4: 2.5: Modem X-10. . . . . . . . . . . . . . . . . . . . . Rede Ethernet. . . . . . . . . . . . . . . . . . . . Rede usando a linha telefônica. . . . . . . . . . . Rede usando a rede de energia elétrica. . . . . . . Last inch access, ou acesso dentro das residências. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 25 25 26 27 Figura 3.1: Diagrama de blocos do ST7538. . . . . . . . . . . . . . . . . . . . 30 Figura 3.2: Modulação FSK. . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Figura Figura Figura Figura Figura Figura Figura Figura Figura Figura Figura Figura Figura Figura Figura Figura Figura 4.1: Divisão do kernel do Linux. . . . . . . . . . . . . . . . . . . . . . 4.2: Adicionando um Driver no Kernel. . . . . . . . . . . . . . . . . . 4.3: Fluxograma da leitura e escrita de dados no ST7538. . . . . . . . 4.4: Seqüência de escrita no registrador interno do ST7538. . . . . . . 4.5: Fluxograma do software de leitura e escrita de dados no ST7538. 4.6: Leitura do registrador interno do ST7538 realizada pelo driver. . . 4.7: Fluxograma do software de leitura e escrita de dados no ST7538. 4.8: Envio de dados para o ST7538. . . . . . . . . . . . . . . . . . . . 4.9: Estrutura do Frame HDLC. . . . . . . . . . . . . . . . . . . . . . 4.10: Envio da seqüência de flags para o ST7538. . . . . . . . . . . . . 4.11: Envio da seqüência de flags para o ST7538 (zoom). . . . . . . . . 4.12: Configuração do Arquivo /etc/ax25/axports. . . . . . . . . . . . . 4.13: Utilizando o kissattach. . . . . . . . . . . . . . . . . . . . . . . . 4.14: Display do ifconfig. . . . . . . . . . . . . . . . . . . . . . . . . . . 4.15: Modem Funcionando. . . . . . . . . . . . . . . . . . . . . . . . . . 4.16: Transmissão de dados para o modem PLC. . . . . . . . . . . . . . 4.17: Recepção de dados pelo o modem PLC. . . . . . . . . . . . . . . 39 41 43 44 45 45 46 46 47 47 48 51 51 52 53 53 54 Figura 5.1: Foto do Modem PLC. . . . . . . . . . . . . . . . . . . . . . . . . 55 LISTA DE TABELAS Tabela Tabela Tabela Tabela Tabela 3.1: 3.2: 3.3: 3.4: 3.5: Ligação Ligação Ligação Ligação Ligação do registrador de controle. dos pinos de entrada. . . . dos pinos de saı́da. . . . . nos multiplexadores. . . . dos pinos de saı́da. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 33 34 35 35 Tabela 4.1: Tipo de frame do protocolo KISS. . . . . . . . . . . . . . . . . . . 49 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 Laboratório de Sistemas de Controle, Automação e Robótica 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 TCP Transmission Control Protocol TEND Transposed Frame End TFESC Transposed Frame Escape TTY Teletype xDSL Digital Subscriber Line 21 1 INTRODUÇÃO A tecnologia PowerLine Communication PLC permite transmitir dados e informações utilizando os cabos da rede elétrica como meio fı́sico. Possuindo assim como principal vantagem sobre as outras tecnologias de transmissão o fato da utilização de uma infra-estrutura fı́sica já existente, pois em praticamente todos os lugares há uma rede elétrica instalada. Isso a torna uma solução prática para as seguintes necessidades: 1. automação residencial, pois, como por necessidade, quase todo o equipamento já está ligado à rede elétrica. 2. telemetria. 3. internet banda larga. Visando tais aplicações, especialmente a telemetria, foi implementada no LASCAR 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 elétrica, utilizando modulação FSK com uma portadora 132.5kHz. Ele possui uma alimentação independente de 10V e a troca de dados com um computador IBM-PC ocorre através da interface da porta paralela. Tais passos serão desenvolvidos no Capı́tulo 3. Para utilizar o modem PLC torna-se necessário o desenvolvimento de um software que controle os sinais do chip. Utilizando um computador rodando Linux, com o kernel 2.6.17, será implementado um driver para a troca de informações do kernel com a aplicação. Tal driver deverá ser responsável por fazer com que que dois modems não transmitam ao mesmo tempo na rede elétrica. Para isso será implementado um controle de acesso ao meio CSMA-ppersitente. Para a camada de enlace, o driver encapsulará os dados em HDLC e utilizará o tipo de transmissão broadband, onde todos os modems recebem o que está sendo transmitido. 1.1 Objetivos O objetivo deste trabalho é explorar as caracterı́sticas do chip ST7538, utilizandoo no desenvolvimento de um modem PLC, implementando o hardware e o software adequados para a tarefa em questão. Os esquemáticos de hardware e a arte final PCB se encontram em anexo no final desse trabalho e podem ser encontrados em <http://www.ece.ufrgs.br/~fetter>. O software do modem terá uma licença GPL, o qual permite ser copiado, distribuı́do e melhorado, sendo desenvolvido para 22 ser utilizado com o kernel 2.6.17, possibilitando que pessoas que necessitem utilizar tal solução possam usufruir de um modem PLC completo para as suas finalidades. A principal motivação deste trabalho é finalizar o desenvolvimento do modem PLC com licença GPL, demonstrando todas as qualidades adquiridas ao longo do curso para tal, para que todas as pessoas possam utilizar tal tecnologia como uma opção viável para a transmissão de dados a baixas velocidades em redes residenciais internas. 1.2 Organização do trabalho Este trabalho está organizado em outros quatro capı́tulos. No Capı́tulo 2 são apresentados as caracterı́sticas da comunicação PLC. O Capı́tulo 3 apresenta as caracterı́sticas do chip ST7538 e dos componentes presentes na placa. No Capı́tulo seguinte, desenvolve-se noções de Linux, funções para acessar ao hardware da placa e dos protocolos que serão suportados pelo o modem e a utilização do software kissattach e a sua com utilidade. 23 2 TECNOLOGIA PLC Este capı́tulo tem por objetivo apresentar aspectos básicos da tecnologia PLC. 2.1 Introdução a Tecnologia PLC A tecnologia PLC usa como meio fı́sico uma das redes mais utilizadas em todo o mundo: a rede de energia elétrica. A idéia desta tecnologia não é nova e isto pode ser comprovado se lembrarmos do antigo sistema ”Babá Eletrônica”que mesmo tendo mais de 30 anos, é utilizado até hoje (IGUAÇU ENERGIA, 2006). Entretanto tal meio possui uma grande quantidade de equipamentos conectados a ele, que interfere na comunicação PLC. Devido a esses problemas, somente há aproximadamente quatro anos, com a evolução da tecnologia, a comunicação PLC começou a ser avaliada e utilizada por algumas empresas. Assim, aproveitando o fenômeno da propagação de ondas através dos condutores elétricos e desenvolvendo uma transmissão de forma controlada (freqüências e nı́veis de propagação especı́ficos) com sinais que utilizam as freqüências de forma inteligente e diferenciada daquelas utilizadas por outros equipamentos e pela própria transmissão da energia elétrica, nasceram os primeiros modems PLC. 2.1.1 X-10 Uma Primeira Idéia Uma das primeiras formas de implementar a comunicação PLC foi através da tecnologia X-10. Ela foi desenvolvida para ser unidirecional, porém atualmente é possı́vel encontrar dispositivos bidirecionais. Os controladores X-10 enviam seus sinais para receptores através da rede de energia elétrica e são utilizado basicamente para controlar lâmpadas e outras aplicação simples. A Figura 2.1 apresenta a foto de um modem comercial PLC utilizando a tecnologia X-10. A tecnologia X-10 utiliza uma forma de modulação Amplitude-Shift Keying (ASK), onde uma portadora modulada de 120kHz com um sinal de 0.5W de energia, é transmitida a cada cruzamento por zero da rede de energia elétrica. Para aumentar a robustez do sistema, cada bit utiliza dois cruzamentos para ser transmitido. A transmissão no cruzamento por zero é justificada, pois neste instante apresenta um menor nı́vel de ruı́do na rede (NETWORKING, 2006). Essa forma de representação do sinal apresenta um pequena largura de banda (60 bps) e fica severamente comprometida se o ambiente de transmissão for muito ruidoso, por esses motivos tal tecnologia ficou limitada a aplicações simples. 24 Figura 2.1: Modem X-10. 2.1.2 Problemas da Tecnologia PLC A tecnologia PLC seria uma solução perfeita se não fosse pelo fato de as linhas de força (assim como a rede telefônica no passado) não serem consideradas meios ideais para a transmissão de dados. Tanto dentro e como fora de casa, a rede elétrica está sujeita a todo tipo de interferência e ruı́dos gerados pela diversidade de equipamentos que estão conectados a ela, tais como fontes chaveadas, motores e dimmers. Esses equipamentos geram durante o seu funcionamento uma série de harmônicas da rede elétrica que pode inviabilizar a comunicação PLC. Outro fator negativo das redes elétricas é sua oscilação: caracterı́sticas como impedância, atenuação podem variar drasticamente de um momento para o outro, à medida que luzes ou aparelhos conectados à rede são ligados ou desligados. Além disso, se a intenção for transmitir informações a longas distâncias, os transformadores de distribuição são verdadeiras barreiras para a transferência de dados. Apesar de permitirem a passagem de corrente alternada a 50 Hz ou 60 Hz com quase 100% de eficiência, os transformadores atenuam seriamente outros sinais de maior freqüência, tal fato foi de fundamental importância para a realização de um modem para a rede interna. 2.1.3 Vantagens da Tecnologia PLC A principal vantagem da tecnologia PLC é a possibilidade de transformar toda a infra-estrutura elétrica de uma residência ou edifı́cio em uma rede local de dados, baseado no conceito de ”aproveitamento da rede elétrica”. A conveniência é até mesmo mais óbvia neste caso porque enquanto nem todo cômodo tiver um telefone para conectar, sempre existirá uma tomada perto de um computador e cada tomada pode ser encarada como um ponto de acesso que pode ser usado de maneira simples e descomplicada. Logo, por não requerer nenhuma instalação nova, a tecnologia PLC é um bom método de conectar computadores em cômodos diferentes. Vale ressaltar que uma rede doméstica PLC não acrescenta custo a conta de energia elétrica. As Figuras 2.2, 2.3, 2.4 apresentam os exemplos de uma rede de dados para uma casa residencial. A Figura 2.2 mostra uma rede Ethernet onde os poucos pontos de acessos estão localizados somente pertos de computadores. A Figura 2.3 mostra uma rede usando a linha telefônica onde existem mais pontos de acessos localizados escassamente por toda a casa. A Figura 2.4 mostra uma rede usando a linha de energia onde existem muitos pontos de acessos localizados por todos os cômodos da 25 casa. Figura 2.2: Rede Ethernet. Figura 2.3: Rede usando a linha telefônica. Essa idéia de transmitir dados sobre rede elétrica também poderia ser aplicada para interconectar dispositivos inteligentes dentro de uma casa. No inı́cio de 2000, a empresa Sunbeam anunciou uma linha de eletrodomésticos inteligentes que trocavam informações no momento em que eram ligados à tomada. Batizada de HLT (Home Linking Technology), a iniciativa pretendia lançar produtos como despertadores, detectores de fumaça, cafeteiras, cobertores elétricos, medidores de pressão arterial, capazes de se comunicar. Por exemplo, o despertador poderia ser programado para mandar uma ordem à cafeteira para começar a preparar o café um pouco antes 26 Figura 2.4: Rede usando a rede de energia elétrica. do pessoal da casa sair da cama. Num futuro próximo, até será possı́vel colocar um filme em DVD no PC da casa e transmitir o som e a imagem para uma TV compatı́vel pela fiação interna (REDES, 2006). 2.2 Tipos de Redes PLC O mercado atual envolvendo a tecnologia PLC encontra-se dividido em dois segmentos: last mile access e last inch access 2.2.1 Last Mile Access Last mile access: seria o acesso até a residências, onde o PLC é somente uma das diversas possibilidades tecnológicas dentre as quais figura o cable modem e diferentes tipos de xDSL e difusão sem fio (broadband wireless). Nenhuma das tecnologias se destaca com relação as demais; nem o PLC apresenta grande vantagens técnicas com relação às outras técnicas, nem estas são muito superiores ao PLC (MAJUMDER; CAFFREY, 2004). É nesse segmento do mercado que as grandes concessionárias estão interessadas, pois a tecnologia PLC usaria as linhas de transmissão das mesmas abrindo um novo setor de mercado para elas. 2.2.2 Last Inch Access Last inch access: seria o acesso dentro das residências. Alguns estudos apontam que o uso de PLC seria mais eficaz que o cabo, ou mesmo o wireless para prover o acesso no interior das residências (MAJUMDER; CAFFREY, 2004), devido a capilaridade da sua rede. O last inch access está conduzindo gradativamente à expansão do espectro das redes caseiras, por meio de um vasto conjunto de equipamentos conectados no interior das habitações por uma rede interna (in-home). Tal rede pode transformar, como dito anteriormente, todas as tomadas elétricas da casa em conexões de difusão para computadores pessoais, telefones, e seus acessórios, bem como para outro dispositivos eletro-eletrônicos, como ilustrado na Figura 2.5. 27 Figura 2.5: Last inch access, ou acesso dentro das residências. 28 29 3 HARDWARE DO MODEM PLC Neste capı́tulo serão expostas as caracterı́sticas do hardware do modem PLC. O hardware do modem é dividido em basicamente duas partes:uma parte analógica constituı́da do chip ST7538 e uma interface dos sinais digitais para a porta paralela de um computador IBM-PC padrão. 3.1 Parte Analógica A parte analógica do modem é constituı́da de um chip ST7538 (PowerLine FSK Transceiver ) e dos componentes necessários para o seu funcionamento. 3.1.1 ST7538-PowerLine FSK Transceiver O chip ST7538 é um transceiver que foi projetado para realizar comunicações seriais half-duplex, como descrito em 3.1.1.1, utilizando a modulação FSK, como 3.1.1.2 sobre as linhas de energia elétrica. Esse chip apresenta uma série de pinos de saı́da para indicar o seu status sob diversas condições, e quatro pinos de entrada para realizar o controle sobre o modem. Internamente apresenta um registrador de 24 bits onde é possı́vel programar diversas opções a cerca do seu funcionamento. Possui uma alimentação analógica de 7.5V a 12.5V e uma alimentação digital de 5V, e necessita de um clock de 16 MHz para o seu correto funcionamento. A Figura 3.1 apresenta o diagrama de blocos do ST7538. 3.1.1.1 Comunicação Half-Duplex Uma comunicação é dita half-duplex (também chamada semi-duplex) quando existem um dispositivo transmissor e outro receptor, sendo que ambos podem transmitir e receber dados, porém não simultaneamente, a transmissão tem sentido bidirecional. Durante uma transmissão half-duplex, em determinado instante um dispositivo A será transmissor e o outro B será receptor, em outro instante os papéis podem se inverter. Esse caso se aplica perfeitamente para as redes de energia elétrica, pois existe apenas um meio de transmissão e o modem envia e recebe na mesma freqüência, logo dois modems não podem transmitir ao mesmo tempo. Caso existisse apenas um modem receptor e um transmissor, tal comunicação seria simplex, ou ainda no caso de que fosse possı́vel que o modem recebesse e transmitisse simultaneamente, tal comunicação seria full-duplex. 30 Figura 3.1: Diagrama de blocos do ST7538. 3.1.1.2 Modulação FSK A modulação FSK também conhecida por modulação de chaveamento de freqüência é um técnica de modulação digital que consiste na mudança da freqüência da onda portadora de acordo com o sinal digital modulante. O modem PLC possui uma onda portadora de alta freqüência, que irá variar +δ para representar uma marca e −δ para representar um espaço. A Figura 3.2 mostra um sinal digital a ser transmitido através dessa modulação e o seu equivalente após a modulação. Figura 3.2: Modulação FSK. Ainda é possı́vel citar que existem a modulação ASK, onde é variada a amplitude da onda portadora, como no caso do X10 2.1.1 e a modulação PSK, onde ocorre uma 31 mudança na fase da portadora, esta modulação é utilizada em modems telefônicos de pequena banda. 3.1.1.3 Registrador Interno Como mencionado anteriormente, o ST7538 possui um registrador interno que possibilita programar algumas opções de funcionamento do mesmo. A leitura e escrita desse registrador é sempre feita de forma sı́ncrona utilizando os pinos REG/DATA, RX/TX, RxD, TxD, CLRT. Para realizar uma leitura nesse registrador é necessário colocar o pino REG/DATA e RX/TX em alto, a seguir deve-se ler o pino RxD a cada borda crescente do clock durante os próximos 24 ciclos, a parte mais significativa do registrador é enviada antes. Para realizar um escrita o procedimento é similar, porém deve-se deixar o RX/TX em baixo e escrever os dados no TxD. Somente os últimos 24 bits serão escritos ao registrador. Através do registrador interno é possı́vel programar algumas opções do ST7538, como a frequência de portadora entre 60kHz até 132.5kHz, velocidade da taxa de transmissão, distância em marca e espaço, etc. 3.1.1.4 Funções Auxiliares O ST7538 apresenta uma série de periféricos para auxiliar o controle e a operação do chip, entre eles podemos destacar: Detector de Banda em Uso: Detecta quando uma freqüência da configuração atual do modem é detectada na linha, está condicionado ao detector de portadora para prevenir alarmes falsos. É diferente do detector de portadora devido a sensibilidade dos seus filtros. Timeout de Transmissão: Após um ou 3 segundos de transmissão contı́nua, o ST7538 é forçado para o modo de recepção conforme o tempo programado no seu registrador interno. Pode se utilizado para controlar o acesso ao meio. Wacthdog de Hardware: O ST7538 possui um watchdog que previne falhas de software assertando o pino RST causando o reset externo e interno da placa. Pode ser programado no registrador interno. Detector de Cruzamento por Zero: É um detector de cruzamento por zero na linha de energia elétrica. Através do registrador interno é possı́vel sincronizar a transmissão com o cruzamento para diminuir os pacotes perdidos. Divisor de Clock: É possı́vel colocar um dos clocks divididos internamente no ST7538 conforme o programado no registrador interno. Protetor de Temperatura: Desliga uma parte do chip ao detectar que a temperatura dele passou de um determinado limite. Regulador de Tensão: Gera uma tensão de 5V para a parte digital do circuito e possui um comparador para verificar a qualidade dessa tensão. 32 3.1.2 Componente Analógicos Para garantir o funcionamento do ST7538 foi construı́do uma interface analógica para o modem. Essa interface é bem similar ao Apliccation Notes do ST7538 (CANTONE, 2003) e é composta basicamente pelos seguintes componentes: Alimentação Digital: É constituı́do de um regulador de tensão 7805 que gera a alimentação de 5V para a parte digital do circuito. A alimentação digital pode ser selecionada dele ou do ST7538 segundo um jumper. Gerador de Clock: É constituı́do de um cristal de 16MHz e seus capacitores para gerar o clock de 16 MHz para o ST7538. Filtros Passa Banda: São filtros sintonizados nas freqüências possı́veis de transmissão. São três filtros, um na entrada do sinal antes do transformador de desacoplamento, um antes da recepção e outro antes da transmissão. Os filtros foram especificados para uma portadora de 132,5kHz. Circuito de Proteção: Composto de três diodos zeners de 6V8 rápidos que protegem o circuito contra spikes da rede elétrica. Controle de Corrente: Composto de um resistor e um capacitor, serve para controlar o valor máximo da corrente nos drivers de saı́da do ST7538. Controle de Tensão: Composto de um divisor de tensão, serve para manter a tensão constante no pino Vsense, esse controle é utilizado para ajustar o ganho do sinal de saı́da do ST7538. Através de um jumper é possı́vel escolher qual será a linha amostrada. Detector de Cruzamento por Zero: Composto de um filtro passa baixa para eliminar as altas freqüências, pode ser conectado ao ST7538 através de um jumper na placa. 3.2 Parte Digital A parte digital do hardware do modem é composta pela a interface dos sinas do ST7538 com a porta paralela de um computador IBM-PC padrão. 3.2.1 Interface com a Porta Paralela A porta paralela é uma interface de comunicação entre um computador e um periférico, que nesse caso será o modem PLC. Ela é basicamente composta de três registradores: registrador de dados, registrador de status e registrador de controle. Cada registrador é composto de 8 bits, porém nem todos eles estão disponı́veis externamente na porta paralela. Neste trabalho ficou definido a utilização dos pinos de dados para enviar sinais para o modem, dos pinos de status para receber sinais e, por fim, do registrador de controle para as demais operações. 3.2.1.1 Pinos de Entrada Através dos seguintes pinos é possı́vel controlar o funcionamento do ST7538: 33 REG/DATA: através desse pino é possı́vel informar ao ST7538 se há o envio dados para serem modulados e transmitidos na rede ou armazenados no seu registrador interno, alterando o seu funcionamento. TxD: Pino de entrada digital, através desse pino passa-se o dado para o chip. Rx/Tx: através desse pino podemos alternar entre uma seção de transmissão ou recepção de acordo com uma comunicação do tipo half-duplex. WD#: Pino de watchdog, esse pino é utilizado para resetar o contador interno do ST7538 e impedir o reset no pino RESET# da placa. Os pinos de entrada do ST7538 estão conectados ao registrador de dados da porta paralela através de um buffer tri-state que está constantemente habilitado (tal buffer é utilizado como uma forma de proteção) e a ligação com os pinos é feita conforme a Tabela 3.2. Tabela 3.1: Ligação do registrador de controle. Pino na porta paralela Bit do registrador AUTOF1 bit1 INIT bit2 Tabela 3.2: Ligação dos pinos de entrada. Pino na porta paralela Bit do registrador Pino no ST7538 Data0 bit0 REG/DAT A Data1 bit1 T xD Data2 bit2 Rx/T x Data3 bit3 W D# 3.2.1.2 Pinos de Saı́da Os pinos de saı́da apresentam diversos status do modem PLC que devem ser constantemente lidos para garantir o controle sobre o mesmo. Os seguintes pinos são lidos do ST7538: RxD: Pino de saı́da digital, através desse pino é possı́vel receber os dados do ST7538. CLRT: Pino de clock digital, através desse pino é possı́vel amostrar o pino RxD de forma correta. MCLK: Pino de clock digital, esse pino possui um clock de alta freqüência que pode ser utilizado para realizar transferências rápidas para o driver. O seu valor depende do registrador interno do ST7538. RESET#: Pino de reset, esse pino é utilizado para que o ST7538 resete o driver devido a uma falha de software, caso o watchdog esteja habilitado. 34 PG: Pino de status da alimentação, esse pino avisa que a tensão de alimentação do ST7538 está adequada através do nı́vel lógico 1. TOUT: Pino de time-out, esse pino é assertado quando uma transmissão durar mais que o valor programado no registrador do ST7538. Esse pino também pode representar que o chip está numa temperatura acima do padrão e que a placa deve ser desligada. REG OK: Pino de status do registrador interno do ST7538, esse pino garante que o conteúdo do registrador interno do chip é válido. CP/PD: Pino de detecção de portadora, através desse pino o ST7538 indica a presença de uma portadora ou preâmbulo (alternância de marcas e espaços) na linha de energia elétrica, conforme o valor do registrador interno do ST7538. BU: Pino de banda em uso, similar ao CP/PD porém com outra sensibilidade. ZCOUT: Pino de detecção de cruzamento por zero na linha de energia, pode ser utilizado para sincronizar a transmissão com a rede de energia. Devido ao fato de que o ST7538 possui mais pinos de saı́da que o registrador de status, eles estão conectados ao registrador compartilhando um barramento através de três buffers tri-state, que são habilitados pelo registrador de controle da porta paralela conforme a o bit desejado para a leitura. Os pinos desse registrador estão conectados conforme a Tabela 3.1. A ligação com os pinos de status é feita conforme a Tabela 3.3. Tabela 3.3: Ligação dos pinos de saı́da. Pino na porta paralela Bit do registrador ERROR ACK SLCT BUSY1 bit3 bit6 bit4 bit7 Pinos no ST7538 ST1 ST2 ST3 CLRT ZCOU T T OU T RESET # RESET # RESET # RxD REG OK BU CD/P D PG M CLK 3.2.1.3 Pinos de Controle Como dito anteriormente os pinos de status são lidos através de um barramento, logo é necessário utilizar os pinos de controle para setar o buffer que utilizará o barramento conforme a Tabela 3.1. A Tabela 3.4 indica como os valores do registradores de controle afetam o barramento de status. 1 Pino invertido 35 Tabela 3.4: Ligação nos multiplexadores. AUTOF INIT ST Ativo 0 0 ST 3 0 1 N enhumST Ativo 1 0 ST 1 1 1 ST 2 3.3 Mudança de Hardware Através do desenvolvimento do software do modem foi constatada a necessidade da mudança no hardware. Para efetuar o recebimento de dados no processador do computador seria necessário ficar realizando polling no ST7538, sendo que tal fato iria consumir muito tempo de processamento dificultando a utilização do modem juntamente com o kernel do Linux. Para corrigir esse defeito foi conectado o pino de clock diretamente no pino 10 (ACK) da porta paralela e a partir deste momento foi utilizado uma interrupção para o recebimento de dados, melhorando o processo. A Tabela 3.5 apresenta a nova configuração dos pinos. Tabela 3.5: Ligação dos pinos de saı́da. Pino na porta paralela Bit do registrador Pinos no ST7538 ST1 ST2 ST3 ERROR bit3 RESET # ZCOU T T OU T ACK bit6 CLRT CLRT CLRT SLCT bit4 RxD REG OK BU 1 BUSY bit7 CD/P D PG M CLK 36 37 4 SOFTWARE DO MODEM PLC Neste capı́tulo serão expostos os passos da construção do software desenvolvido para controlar o modem PLC. Para que sejam compreendidas os passos descritos neste relatório, é conveniente que haja uma breve explicação dos conceitos envolvidos nas atividades. Uma pequena introdução sobre sistemas operacionais, particularmente sobre o sistema Linux, será fornecida. Também, uma breve descrição da linguagem C utilizada no desenvolvimento do software do modem PLC. A seguir será explicado o desenvolvimento do software do modem PLC. 4.1 Plataforma de Desenvolvimento do Software Como opções para plataforma de desenvolvimento do driver para computadores IBM-PC há como principais alternativas o Windows e o Linux. No Windows não há acesso aos códigos fontes do sistema operacional e exitem algumas dificuldades de acessar alguns dispositivos de I/O como a porta paralela, por exemplo. Já na plataforma Linux, os códigos fontes do mesmo são acessı́veis, o que possibilita qualquer alteração que poderá eventualmente ser necessária, e existe acesso a todos os dispositivos do computador. Por esse motivo o Linux é utilizado amplamente no desenvolvimento de aplicações embarcadas, tornando-se a plataforma de desenvolvimento ideal para o driver tanto para o seu desenvolvimento como para a sua aplicação. Com a escolha da plataforma de desenvolvimento do driver, torna-se necessário a escolha do tipo de driver que será desenvolvido. Inicialmente pode-se pensar em implementar um simples driver para dispositivos de caracter para Linux. A primeira vista essa alternativa é uma boa opção pois implementação de um driver char é simples e fácil de ser compreendida evitando maiores problemas. Drivers char funcionam de forma similar a arquivos em linguagem C. Porém pelo fato de que o modem implementa uma comunicação serial, a implementação de tal driver implicaria na necessidade de desenvolvimento de complexos programas de aplicações para a sua utilização. Para realizar comunicação serial torna-se adequando o desenvolvimento de um driver TTY para Linux pois tal driver apresenta certas funções adequadas a essa comunicação e possuem uma série de programas já prontos para a sua utilização. Porém ele apresenta uma implementação um pouco mais complexa que um driver char. Assim o desenvolvimento de um driver TTY torna-se adequado nessa situação para tornar o resultado mais útil ao usuário final. Devido ao fato de que o modem realiza uma comunicação half-duplex torna-se necessário implementar algum controle de acesso á linha de transmissão no driver para evitar que dois modems tentem transmitir ao mesmo tempo, o que conseqüente- 38 mente inviabilizaria a comunicação. Nesse trabalho será implementado um controle de fluxo CSMA-ppersitente, pois ele possui as caracterı́sticas necessárias para essa aplicação e permite uma maior eficiência do meio de transmissão. 4.1.1 Sistemas Operacionais Em um sistema microprocessado complexo, vários processos são executados. Em um sistema multitarefa, o processador deve atender a todos os processos em um intervalo reduzido de tempo, de forma que para um usuário do sistema, eles pareçam ser executados simultaneamente. Todos os processos demandam recursos do sistema, como memória, conexão de rede, tempo de computação, acesso a dispositivos etc. O sistema operacional pode ser entendido como uma camada de software, colocada entre a aplicação e o hardware, responsável por gerenciar a execução dos processos e suas demandas. Na maior parte do tempo, o processador executa aplicativos. O sistema operacional é chamado na ocorrência de um evento especial, que pode ser uma chamada de sistema ou uma interrupção de periférico. A chamada de sistema merece atenção especial, pois é a interface através da qual o aplicativo faz uma solicitação de serviço ao sistema operacional. Uma chamada de sistema é como uma chamada de subrotina, mas que transfere a execução para o sistema operacional. Ao seu retorno, a execução do aplicativo é retomada na instrução seguinte à chamada. A parte do sistema operacional responsável pela implementação das chamadas de sistema é chamada de núcleo (kernel ) do sistema. O kernel gerencia o acesso ao processador, à memória, aos dispositivos de entrada e saı́da e ao sistema de arquivos. 4.1.1.1 Linux O sistema operacional Linux é um sistema Unix-like, tendo muitas caracterı́sticas comuns a outros sistemas UNIX. Uma virtude do sistema Linux é ser um sistema operacional independente de plataforma, ou seja, não está atrelado a processadores Intel e compatı́veis. O sistema Linux pode ser utilizado, por exemplo, em processadores ARM, Alpha, PowerPC, M68K, MIPS, SPARC, S390 e outros, que constituem em uma alternativa à arquitetura IA32 de processadores (RUBINI; CORBET, 2001). Em um sistema Unix-like, existem diversas tarefas sendo executadas. Cada tarefa requisita por recursos do sistema operacional, tais como, processamento, memória, conectividade de rede e outros recursos especı́ficos. O kernel do Linux é um grande programa que se encarrega de distribuir tais recursos entre as tarefas. O kernel pode ser dividido, como mostra a Figura 4.1, nas seguintes partes: Gerenciador de Tarefas: O kernel é encarregado de destruir e criar tarefas, bem como tratar da sua conexão com o mundo externo. A comunicação entre diferentes tarefas é a base para o correto funcionamento do sistema operacional e também é tratado pelo kernel. Ainda existe o escalonador (schelduler ), que controla como as tarefas dividem o processamento da CPU nesta parte do kernel. Gerenciador de Memória: A memória do computador é um recurso essencial, e a polı́tica para o seu uso é crucial para o desempenho do sistema operacional. O kernel constrói um espaço de endereçamento virtual para si e para todas as tarefas. Através de tal endereçamento é feita a divisão entre kernel space e user 39 Figura 4.1: Divisão do kernel do Linux. space. O sistema operacional, que roda no kernel space, gerencia a memória contra acessos não autorizados. Esta tarefa de proteção é feita com o auxilio do hardware do processador. De fato, todos os processadores modernos possuem nı́veis (ou modalidades) diferentes de operação, e algumas ações apenas são permitidas em certos nı́veis de operação. Sistemas de Arquivo: Unix é fortemente baseado na conceito de sistemas de arquivo. Praticamente tudo em um sistema Unix pode ser tratado como um arquivo. O kernel constrói um sistema de arquivos estruturados em cima de uma hardware desestruturado, e o resultado dessa abstração é usado por todo o sistema. Ainda. o Linux suporta múltiplos sistemas de arquivo de diferentes tipos, sendo que, cada tipo de sistema utilizada uma forma diferente de organizar os dados no meio fı́sico. Como exemplo de sistemas de arquivos existem: EXT2, EXT3, FAT, NTFS, JFFS2 e Reiser. Controlador de Dispositivos: Cada operação do sistema operacional, geralmente, é direcionada para um dispositivo fı́sico diferente. Com exceção do processador, memória, e outras poucas entidades, todas as operações direcionadas a um hardware são executadas por um código especı́fico para aquele dispositivo. Tal código é chamado de driver. O kernel deve ter um driver especı́fico para cada periférico presente no sistema, desde HD até o teclado. Por esse motivo será desenvolvido um driver para tratar as funções do modem PLC. Existem diversos tipos de drivers, porém eles não serão abordados nesse trabalho. Sistema de Rede: As operações de rede devem ser controladas pelo sistema ope- 40 racional porque tais operações não são uma especificas a uma tarefa. Pacotes chegando são eventos assı́ncronos que devem ser coletados, identificados e despachados antes que uma tarefa precise deles. O sistema é responsável por entregar os pacotes á sua devida tarefa, ou á sua interface de rede, e controlar a execução dos programas de acordo com a sua atividade na rede. Uma grande vantagem do Linux, é que o seu kernel já possui desenvolvido controles de diversos protocolos como PPP, AX-25, FR, SLIP, PLIP, X25 e outros. Assim tais protocolos são gerenciados pelo kernel sendo transparentes para o modem PLC. Neste ponto será implementado um driver TTY no kernel do Linux, controlado pelo controlador de dispositivos, que utilizará as operações de rede para realizar a comunicação entre dois modems. 4.2 Linguagem C A linguagem C tem sido utilizada com sucesso nas mais diversas aplicações, tais como sistemas operacionais, compiladores e utilitários em geral. A popularidade da linguagem aumentou nos últimos anos, tornando C uma linguagem de propósito geral. O código C é bastante portável. Isso significa que softwares escritos para um determinado processador podem ser facilmente transmitidos para um outro de arquitetura diferente. O sistema operacional UNIX, do qual o Linux é derivado, é escrito em C. Na verdade, a linguagem C foi desenvolvida para que se implementasse o sistema UNIX. Portanto, é natural que C seja a linguagem utilizada para o desenvolvimento de aplicativos para o sistema Linux. A principal diferença entre a linguagem C e as outras linguagens de programação reside no emprego de ponteiros, ou seja, no endereçamento indireto. Os programas são estruturados através da definição e chamada de funções. Dados podem ser armazenados em arranjos (arrays) ou estruturas. Na comparação com outras linguagens, C é considerado de nı́vel médio, pois oferece um bom grau de abstração ao programador (caracterı́stica de linguagens de alto nı́vel), ao mesmo tempo em que permite acesso eficiente ao hardware (como em linguagens de baixo nı́vel) (SCHILDT, 1988). Distribuições Linux oferecem um compilador C, o GCC, que foi o compilador utilizado para gerar o driver. 4.2.1 Programando no Kernel do Linux Esta parte tem como objetivo introduzir alguns conceitos de programação para o kernel do linux. Todas as funções utilizadas no drivers podem ser achadas no código fonte do kernel -2.6.17 localizados nos headers do mesmo. Inicialmente como o kernel roda por si mesmo, ele não pode utilizar funções da bibliotecas padrões do C e sim as suas próprias. A função printf() é substituı́da pela printk() que está definida no kernel de modo bastante similar ao printf(). O driver pode utilizar printk() pois, após ele ser linkado com o kernel, ele tem acesso a todas os sı́mbolos públicos do kernel (funções e variáveis). Indo adiante, existem outras diferenças entre o driver do kernel e uma aplicação. Onde uma aplicação faz uma simples tarefa do inı́cio ao fim, um driver 41 deve-se registrar para prover futuras chamados do kernel ao mesmo. A função init_module() e as outras chamadas por ela são responsáveis pela inicialização do driver e por informar ao kernel a sua existência e as suas funcionalidades. A função cleanup_module() faz o contrário. É possı́vel adicionar um driver ao kernel já carregado utilizando os programas insmod e rmmod para adicionar e retirar, respectivamente, drivers e módulos no kernel. Deve-se executar tais programas como superusuário para que a operação seja feita com sucesso. A Figura 4.2 mostra como as funções e os ponteiros são utilizados para adicionar uma nova funcionalidade ao kernel rodando. Figura 4.2: Adicionando um Driver no Kernel. Como mencionado anteriormente, a divisão do espaço do usuário e do kernel é implementada pelo controlador de memória, e assim deve-se usar funções especı́ficas para tratar ponteiros recebidos do programa de aplicação. A função put_user() é utilizada para colocar dados no espaço do usuário e get_user() recebe dados do mesmo. Somente funções que são realmente parte do kernel podem ser usada no driver. Qualquer header pode ser encontrado em include/linux e include/asm dentro da árvore do kernel. Como até mesmo o menor driver é linkado a todo o kernel do Linux, deve-se tomar o cuidado no uso de variáveis globais. Caso existam duas variáveis globais com o mesmo nome, podem ocorrer os mais variados tipos de falhas, que dependerão da configuração do computador usado e dos módulos carregados no kernel. Para evitar tais problemas recomenda-se a utilização da declaração static para as suas variáveis. 42 Por fim, uma última diferença em a programação no kernel e na aplicação é a respeito de erros no programa. Enquanto um uma falha de segmentação na aplicação é simplesmente um aviso e um debbuger pode ser utilizado para rastrear o problema, no kernel, tal problema é fatal não só para a aplicação que está sendo executada e sim para todo o sistema. Métodos de depuração para o kernel existem, porém eles não serão tratados neste trabalho. No desenvolvimento do driver foi utilizado o programa oops para depurar tais problemas. 4.3 Especificações de Hardware Após apresentar alguns conceitos a respeito de Linux é possı́vel tratar do desenvolvimento do driver em si. O hardware construı́do influencia fortemente o funcionamento do driver, pois algumas opções do registrador interno do ST7538 não poderão ser alteradas pelos usuários do modem. As opções fixas são as seguintes: Freqüência da portadora: Os filtros localizados no hardware da placa foram sintonizados para um freqüência de portadora de 132.5KHz. Logo essa opção fica fixa. Watchdog: Não será implementada uma função de watchdog nesta versão de software e portanto ele deve permanecer desabilitado. Timeout: O timeout de transmissão será implementado em software nas funções de protocolo, logo o de hardware deve ser desabilidato. Detecção de portadora: Como será utilizado o pino de clock para interromper o processador, o clock só deverá ser passado para o processador quando houver algum dado disponı́vel tornando essa opção fixa. Interface Sı́ncrona: Não será implementada um software que suporte a interface assı́ncrona, logo essa opção será fixa. Modo Pacote: Essa função foi retirada na primeira revisão do datasheet do ST7538 e não será desenvolvido suporte para ela. As demais opções como taxa de transmissão e outras ficam como opções de funcionamento para o usuário. 4.4 Kernel Space: Driver TTY Como citado em 4.1.1.1, foi implementado um driver TTY no kernel do Linux para realizar a parte de baixo nı́vel do modem PLC. Ele deverá se comunicar com o usuário com um programa de aplicação conforme os passos descrito em 4.5. Ele possui basicamente as seguintes funções: 1. funções de acesso ao hardware, 2. funções de protocolos, 3. funções de drivers TTY. O código fonte do driver se encontra em anexo no final desse trabalho. 43 4.4.1 Funções de Acesso ao Hardware As funções dessa seção serão responsáveis por realizar o acesso em mais baixo nı́vel ao hardware do modem PLC. Elas deverão dar suporte as funções das camadas superiores para o gerenciamento do driver. 4.4.1.1 Função de Leitura dos Pinos do ST7538 Para realizar a leitura e a escrita nos pinos de dados e de controles do ST7538 basta realizar a leitura dos pinos, modificar os pinos desejados e então escrever o valor no respectivo registrador. Para realizar a leitura dos pinos de status do ST7538 será criada a função readMSR(), que deve seguir o seguir o fluxograma da Figura 4.3. Figura 4.3: Fluxograma da leitura e escrita de dados no ST7538. Primeiramente é necessário setar um dos buffers do barramento, realizar a leitura dos seus pinos de status, armazenar em um registrador. Então se deve setar outro buffer e fazer todos os passos novamente. Assim é realizada a leitura dos pinos de status do ST7538. 44 4.4.1.2 Função de Leitura e Escrita do Registrador Interno do ST7538 Essas funções devem ser responsáveis por ler e escrever a configuração desejada no registrador interno do ST7538. Isso é feito através da Figura 4.4 Figura 4.4: Seqüência de escrita no registrador interno do ST7538. Devido ao fato de que a configuração do modem não é realizada durante o funcionamento do driver, ou não frequentemente, implementamos funções que realizam polling no pino de clock do ST7538. As funções readRegister() e writeRegister() implementam os passos do fluxograma da Figura 4.5 para fazer sua função. Inicialmente é necessário setar o pino REG/DATA do ST7538, a seguir deve-se ler e escrever os próximos 24 bits de configuração do modem PLC. A Figura 4.6 mostra a visualização de uma leitura do registrador interno do ST7538 realizada pelo driver. Nota-se o clock variando rapidamente em baixo e o bits no registrador sendo escritos em cima. 4.4.1.3 Função de leitura e escrita dados do ST7538. Essas funções devem ser responsáveis por enviar e receber dados do ST7538. Os passos para o recebimento dos dados são similares à escrita e a leitura do registrador interno do ST7538, porém ela é realizada de uma forma totalmente diferente das anteriores. O recebimento/escrita de dados é realizada em um contexto de interrupção do processador e seguem os passos do fluxograma representado na Figura 4.7. A cada ciclo de clock recebido, um dado é lido/escrito para o ST7538. A cada oito bits que isso ocorre um novo dado é carregado para repetir o processo ou um dado é enviado para a camada superior do driver. A Figura 4.8 mostra um frame sendo enviado para o hardware do modem PLC. Inicialmente um preâmbulo é enviado para que o ST7538 possa detectar a portadora na recepção e assim garantir que nenhum bit será perdido.. 4.4.2 Funções de Protocolos Para aumentar a robustez do sistema serão implementados dois nı́veis de protocolos: HLDC e CSMA-ppersistente. Por uma questão de facilidade para o usuário será implementado também o protocolo KISS como explicado em 4.5. 4.4.2.1 Funções de HDLC HDLC é um dos protocolos mais utilizados da camada dois do modelo OSI (TANENBAUM, 2003), sendo estruturado em frames. A Figura 4.9 mostra a estrutura 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. 46 Figura 4.7: Fluxograma do software de leitura e escrita de dados no ST7538. Figura 4.8: Envio de dados para o ST7538. 47 de um frame HDLC. Figura 4.9: Estrutura do Frame HDLC. HDLC utiliza inserção/remoção de zeros (bit-stuffing) para garantir que no pacote de dados não apareça a flag (7E), que por sua vez serve para delimitar o inı́cio e o final de cada frame. É possı́vel visualizar a transmissão dos flags para o modem na Figura 4.10 e na Figura 4.11. Apesar de o frame original HDLC possuir um campo de endereço, esse não será utilizado neste projeto em questão. Isso se deve ao fato de que o campo de endereço só é realmente útil quando existe um hardware que permite a decodificação do endereço, o que não se aplica a esse caso. Por isso a transmissão será broadcast, ou seja, todos os modems escutam todos os modems e uma camada de cima deverá ser responsável por descartar as mensagens que não são para aquele modem. Figura 4.10: Envio da seqüência de flags para o ST7538. Após acabar o pacote o software deverá adicionar 16 bits de CRC para garantir a integridade dos dados durante a transmissão. Para as funções de CRC foram incluı́dos trechos de códigos prontos. Se durante a mensagem ocorre a presença de sete bits ”um”consecutivos o frame é abortado. A função hdlc_tx_byte() é responsável pela a máquina de estados da transmissão do frame HDLC, isto é, pela inserção de flags, do CRC e do bit-stuffing da transmissão. A função hdlc_rx_byte() é responsável pela a recepção do frame, e pela verificação do bit-stuffing durante o frame, os flags são tratados pela a função 48 Figura 4.11: Envio da seqüência de flags para o ST7538 (zoom). hdlc_rx_add_bytes() e o CRC pela a função hdlc_rx_flag(). Essa ultima função também coloca o dado em um pacote KISS como será explicado em 4.4.2.3. 4.4.2.2 Funções de CSMA-ppersistente O CSMA, é um protocolo de telecomunicação que organiza a forma como os modems compartilham um determinado meio de transmissão. Este protocolo está baseado na detecção da portadora para controlar quando o meio está sendo utilizado. O CSMA-ppersistente é uma variação do CSMA na qual ao detectar que o meio está disponı́vel para a transmissão, cada modem tem uma probabilidade p de ganhar o acesso ao meio. Utilizando tal técnica é possı́vel diminuir o número de colisões de acordo com uma rede especifica em questão. Como o modem PLC utiliza uma comunicação half-duplex é necessário realizar um controle de acesso ao meio fı́sico, ou seja, a linha de energia elétrica. O acesso ao meio será realizado através de timeslots, assim cada modem PLC terá um determinado tempo para verificar o estado do meio. Caso o meio esteja livre, ele irá realizar um sorteio para tentar ganhar o meio. Ao final de cada transmissão do frame o meio será liberado para o sorteio entre os modems escutando a linha. Cada modem também deverá enviar uma certa quantidade de flags após o final e antes do inı́cio da mensagem. Todos esses parâmetros são passados para o driver na sua inserção junto ao kernel de Linux. Para realizar o controle de timeslots será inicializado no kernel um timer que periodicamente irá verificar o estado da linha e caso ela esteja livre, cada modem que está disputando o meio terá uma probabilidade P de conseguir pegar o meio para si. Caso dois ou mais modem peguem a linha ao mesmo tempo, ocorrerá colisão e assim sendo que nenhum dos modems conseguirá transmitir corretamente o seu 49 pacote. Como não é possı́vel detectar colisões com o hardware do modem PLC, não será feito nenhum controle de transmissão nesse nı́vel do driver. O nı́vel de cima deverá ser responsável por controlar se o frame foi entregue ou se ocorreu colisão. A função arb_sched() é responsável por verificar a disponibilidade do meio ao final de cada timeslot e caso necessário chamar a função tx_arbitrate() onde ocorre o sorteio do acesso ao meio. O sorteio é feito através da função rand() implementada de (METAWARE, 1994). 4.4.2.3 Funções de KISS O protocolo KISS foi proposto em 1986 por Phil Karn (KARN, 1986) para o seu software TCP-IP. Esse protocolo também é baseando em frames, sendo que o inı́cio e o final de cada frame é seguido de um caractere especial chamado FEND similar ao flag do HDLC, porém nenhum controle de erro como CRC é implementado. Se o caractere FEND aparece durante os dados, ele é convertido em dois caracteres especiais em seqüência, FESC TEND. Assim como se o FESC aparece nos dados, ele é convertido em FESC TFESC. O caractere FEND não é enviado entre os dados para garantir a integridade dos frames. Esse protocolo é bastante similar ao SLIP. Para distinguir comandos de dados, o primeiro byte de cada frame indica o tipo de frame que será. A Tabela 4.1 indica o tipo de frame em função do comando. Tabela 4.1: Tipo de frame do protocolo KISS. Valor do bit Tipo de Frame 0x0 DATA FRAME 0x1 0x2 0x3 0x4 TXDELAY P TIMESLOT TXTAIL Função o resto do frame contém dados a ser passados para o próximo nı́vel o próximo byte contém o txdelay em 10ms o próximo byte contém o parâmetro ppersistente o próximo byte contém o timeslot em 10ms o próximo byte contém o txtail em 10ms Esses parâmetros foram descritos anteriormente em 4.4.2.2 e podem ser alterados com o driver já carregado através do protocolo KISS. A função lpmodem_put_char() é responsável por realizar a decodifição dos caracteres especiais bem como os macros ADD_CHAR() e ADD_KISSCHAR() por codificar os mesmos. A função lpmodem_put _fend() deverá decodificar o comando do protocolo KISS fazendo a alteração do parâmetro quando necessário. Por fim as funções store_kiss_packet(), ack_packet(), get_packet() e store_packet() fazem a interface com o buffer circular em que os dados vindo desse protocolo são colocados para serem encapsulados em HDLC. A questão do uso do protocolo KISS será explicada no decorrer desse trabalho em 4.5. 4.4.3 Funções de Driver TTY Como comentado anteriormente a interface do modem PLC será implementada em plataforma Linux através de um driver TTY. Basicamente o driver TTY deve possuir as seguintes funções: Funções de Inicialização e Finalização: Tais funções são chamadas na inicialização/finalização do driver, elas devem alocar memórias para buffers, realizar a 50 configuração default do modem, requisitar a porta paralela, registrar o driver, etc. Função de Ioctl: Essa é a forma básica do usuário modificar a configuração do modem. Através dele deverá ser possı́vel escrever e ler a configuração do registrador interno do ST7538, configurar os parâmetros do protocolo KISS, etc. Funções de Escrita: O driver deve receber dados através dessas funções. Devido à forma que ele está implementado é necessário que esse dado esteja encapsulado com o protocolo KISS. Funções de Leitura: Essa é a forma que o driver envia dados para o usuário. Os dados serão enviados utilizando protocolo KISS. Funções de Buffers: Essas funções devem responder ao usuário informações sobre os buffers, tais como quantos caracteres estão neles ou quanto espaço há livre para aceitar novos caracteres. 4.5 User Space: kissattach A escolha de um driver TTY para o kernel do Linux foi feita especialmente pensando nesta parte do trabalho. Devido a grande utilidade de tais drivers para a comunicação serial, já foram desenvolvidos programas de aplicação que se conectam a eles, implementado assim um modem completo. O kissattach faz parte do pacote ax25-util utilizado por rádios amadores, desenvolvido para funcionar com o kernel do Linux. AX.25 é um protocolo para redes de rádio amador, que nesse caso é utilizado para um rede PLC, ele deve ter o seu suporte compilado juntamente com o as operações de redes ,citadas em 4.1.1.1. do kernel para que o programa de aplicação seja suportado. No caso de um radio amador a comunicação também é half-duplex, onde o ar é o meio fı́sico e somente um radio pode falar de cada vez no meio, por essa similaridade com a comunicação PLC tal protocolo também será utilizado no modem PLC. Através desse programa é possı́vel emular um ponto IP, utilizando controle de fluxo TCP, no computador. Esse programa se conecta a um driver TTY do Linux utilizando o protocolo KISS. e assim, justifica-se a utilização de um driver TTY juntamente com o protocolo KISS na implementação do modem. Devido ao fato de que ele utiliza o protocolo TCP-IP, não precisamos nos preocupar com a questão de endereçamento ou de controle de colisão pois tal protocolo irá descartar pacotes que não sejam para si e também irá se comprometer a retransmitir pacotes perdidos. Assim será possı́vel emular uma placa de rede através do modem PLC, porém vale ressaltar que a velocidade será baixa. Como dito anteriormente, para utilizar o kissattach é necessário ter compilado o kernel com suporte ao protocolo AX.25, ter instalado a biblioteca ax25 e possuir o 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 informações sobre configuração do meio fı́sico como: name é um identificar para o meio, 51 Figura 4.12: Configuração do Arquivo /etc/ax25/axports. callsign contém uma identificação para radio amador e não precisa ser alterada, speed é a velocidade do meio e deve ser setada de acordo com o que o modem suporta, paclen é o tamanho máximo do pacote ax25 a ser transmitido, windows, é o tamanho da janela do protocolo ax25, ou seja, quantos pacotes ele irá enviar anter de receber uma confirmação, description bem é a descrição. Como o arquivo /etc/ax25/axports configurado é possı́vel executar o programa da aplicação. A Figura 4.13 demonstra um exemplo de como executar o programa. Os argumentos são os seguintes: Figura 4.13: Utilizando o kissattach. /dev/ttyPLC é o device associado ao major do driver TTY, 52 1 identificador que está no arquivo /etc/ax25/axports, 192.168.0.1 endereço IP. Após esses passos, pode-se confirmar a configuração e o endereço IP utilizando o comando ifconfig conforme a Figura 4.14. Para verificar o estado do link utilizamos o comando ping conforme a Figura 4.15 Assim podemos afirmar que o kissattach está utilizando as funções do kernel para empacotar os dados com TCP/IP sobre AX.25 e após sobre KISS. A seguir o driver do Linux é chamado juntamente com as funções de protocolo kiss para se comunicar com as funções de protocolo HDLC. Essas últimas funções serão chamadas quando as funções de CSMA permitirem o acesso ao meio. Por fim, após os dados serem encapsulados em HDLC as funções de acesso ao hardware são chamadas passando a informação para o modem e completando o ciclo. Figura 4.14: Display do ifconfig. A Figura 4.16 mostra a transmissão para o hardware dos dados encapsulados com o protocolo HDLC oriundos do comando ping direcionado para a rede criada com o programa kissattach. Pode-se perceber a colocação de flags do HDLC antes dos dados passados para o driver. A Figura 4.17 apresenta a recepção do frame pelo o modem receptor, de forma semalhente a transmissão como deveria ocorrer. 53 Figura 4.15: Modem Funcionando. Figura 4.16: Transmissão de dados para o modem PLC. 54 Figura 4.17: Recepção de dados pelo o modem PLC. 55 5 CONCLUSÃO Os conhecimentos adquiridos durante os três anos de trabalho como bolsista de iniciação cientifica foram fundamentais para a consolidação do aprendizado da faculdade e foram responsáveis pelo desenvolvimento do esquemático e da placa PCB do 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 comunicação PLC foi adequada, tendo em vista que mesmo com a grande quantidade de ruı́do na rede elétrica a comunicação ocorre sem maiores problemas. O protocolo HDLC, com o conceito de frames, bit-stuffing, e CRC permite a eliminação de pacotes errados e o protocolo TCP/IP é responsável por retransmitir tais pacotes, bem como cuidar do endereçamento. O controle de acesso ao meio CSMA-ppersistente se mostrou uma boa alternativa para uma rede PLC. 56 Uma questão que poderia ser repensada seria o fato de ter utilizado um microprocessador menor no controle do modem PLC, tal modificação evitaria o ocorrências de interrupções no computador e aumentaria o desempenho do mesmo. Porém tal caminho não foi adotado devido ao fato de diminuir a complicação em hardware e resolver em software os problemas, o fato do driver esta funcionando também indica que a escolha tomada foi viável. Uma futura alteração que pode ser proposta neste trabalho é a implementação de um driver de rede para Linux. Tendo em vista que o objetivo era aprender a usar o ST7538, os resultados foram satisfatórios. Por fim, vale ressaltar que o modem PLC desenvolvido neste relatório é apenas um protótipo inicial, e como tal está sujeito a uma série de BUGs que só serão descobertos e possivelmente corrigidos no decorrer da utilização do mesmo. Neste trabalho foi construı́da uma rede PLC composta de dois modems e conseguimos comunicar um com o outro através do comando ping. Esse trabalho fica disponı́vel a todas as pessoas que quiserem formar um rede PLC com as ressalvas acima. 57 REFERÊNCIAS 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 Description. STMicroeletronics, 2003. Application Note. IGUAÇU 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, Califórnia, EUA, 1986. MAJUMDER, A.; CAFFREY, J. Power Line Communications: an overview. IEEE Potentials, vol.23, 2004. METAWARE. Metaware High C Reference Manual. Santa Cruz, Califórnia, 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 Usuário. 2th.ed. São Paulo, 1988. TANENBAUM, A. Redes de Computadorees. 3th.ed. 2003. 58 59 APÊNDICE A DEM PLC DIAGRAMAS ESQUEMÁTICOS DO MO- 60 61 62 63 APÊNDICE B ARTE FINAL DO PCB DO MODEM PLC 64 65 66 67 68 69 APÊNDICE C DEM PLC CÓDIGO FONTE DO DRIVER DO MO- /******************************************************************************* 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 #include #include #include #include #include #include #include #include #include #include <linux/delay.h> <linux/errno.h> <linux/fs.h> <linux/interrupt.h> <linux/ioport.h> <linux/mm.h> <linux/sched.h> <linux/workqueue.h> <linux/tty.h> <linux/tty_flip.h> <linux/parport.h> #include <st7538.h> #include <lpmodem.h> //lpmodem defines 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 static static static static static static int int int int int int int parport[4]={0xffffffff,0xffffffff,0xffffffff,0xffffffff}; defbaud[4]={DFLT_BAUD,DFLT_BAUD,DFLT_BAUD,DFLT_BAUD}; tx_delay[4]={20,20,20,20}; tx_tail[4]={2,2,2,2}; slottime[4]={10,10,10,10}; ppersist[4]={63,63,63,63}; 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); 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= BH_SCHED_TX= }; struct access_params { int tx_delay; /* int tx_tail; /* int slottime; /* int ppersist; /* int fulldup; /* 0x01, 0x02 the transmitter keyup delay in 10ms units */ the transmitter keyoff delay in 10ms units */ the slottime in 10ms; usually 10 = 100ms */ the p-persistence 0..255 */ the driver does not support full duplex, setting this just makes the driver send even if DCD is on */ }; #define BH_RX_BUFLEN #define BH_TX_BUFLEN 256 16 struct hdlc_state_rx { int rx_state; unsigned int bitstream; unsigned int bitbuf; int numbits; /* bottom half RX buffer */ /* bottom half TX buffer */ /* 0 = sync hunt, != 0 receiving */ /* keep track of bitstream to catch stuffed bit */ /* hdlc input bit buffer */ /* 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; int numbits; /* buffer for hdlc output bits */ /* 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 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 static static static static static static unsigned unsigned unsigned unsigned unsigned unsigned unsigned char rx_bit_buff; char tx_bit_buff; char rxcont; char txcont; char plc_fixme; char plc_fixme2; int aaa=0; static void load_txbitbuff(struct lpmodem_state *lpmodem); /******************************************************************************* The CRC routines are stolen from WAMPES by Dieter Deyke *******************************************************************************/ 73 static const { 0x0000, 0x8c48, 0x1081, 0x9cc9, 0x2102, 0xad4a, 0x3183, 0xbdcb, 0x4204, 0xce4c, 0x5285, 0xdecd, 0x6306, 0xef4e, 0x7387, 0xffcf, 0x8408, 0x0840, 0x9489, 0x18c1, 0xa50a, 0x2942, 0xb58b, 0x39c3, 0xc60c, 0x4a44, 0xd68d, 0x5ac5, 0xe70e, 0x6b46, 0xf78f, 0x7bc7, }; unsigned short crc_ccitt_table[]= 0x1189, 0x9dc1, 0x0108, 0x8d40, 0x308b, 0xbcc3, 0x200a, 0xac42, 0x538d, 0xdfc5, 0x430c, 0xcf44, 0x728f, 0xfec7, 0x620e, 0xee46, 0x9581, 0x19c9, 0x8500, 0x0948, 0xb483, 0x38cb, 0xa402, 0x284a, 0xd785, 0x5bcd, 0xc704, 0x4b4c, 0xf687, 0x7acf, 0xe606, 0x6a4e, 0x2312, 0xaf5a, 0x3393, 0xbfdb, 0x0210, 0x8e58, 0x1291, 0x9ed9, 0x6116, 0xed5e, 0x7197, 0xfddf, 0x4014, 0xcc5c, 0x5095, 0xdcdd, 0xa71a, 0x2b52, 0xb79b, 0x3bd3, 0x8618, 0x0a50, 0x9699, 0x1ad1, 0xe51e, 0x6956, 0xf59f, 0x79d7, 0xc41c, 0x4854, 0xd49d, 0x58d5, 0x329b, 0xbed3, 0x221a, 0xae52, 0x1399, 0x9fd1, 0x0318, 0x8f50, 0x709f, 0xfcd7, 0x601e, 0xec56, 0x519d, 0xddd5, 0x411c, 0xcd54, 0xb693, 0x3adb, 0xa612, 0x2a5a, 0x9791, 0x1bd9, 0x8710, 0x0b58, 0xf497, 0x78df, 0xe416, 0x685e, 0xd595, 0x59dd, 0xc514, 0x495c, 0x4624, 0xca6c, 0x56a5, 0xdaed, 0x6726, 0xeb6e, 0x77a7, 0xfbef, 0x0420, 0x8868, 0x14a1, 0x98e9, 0x2522, 0xa96a, 0x35a3, 0xb9eb, 0xc22c, 0x4e64, 0xd2ad, 0x5ee5, 0xe32e, 0x6f66, 0xf3af, 0x7fe7, 0x8028, 0x0c60, 0x90a9, 0x1ce1, 0xa12a, 0x2d62, 0xb1ab, 0x3de3, 0x57ad, 0xdbe5, 0x472c, 0xcb64, 0x76af, 0xfae7, 0x662e, 0xea66, 0x15a9, 0x99e1, 0x0528, 0x8960, 0x34ab, 0xb8e3, 0x242a, 0xa862, 0xd3a5, 0x5fed, 0xc324, 0x4f6c, 0xf2a7, 0x7eef, 0xe226, 0x6e6e, 0x91a1, 0x1de9, 0x8120, 0x0d68, 0xb0a3, 0x3ceb, 0xa022, 0x2c6a, 0x6536, 0xe97e, 0x75b7, 0xf9ff, 0x4434, 0xc87c, 0x54b5, 0xd8fd, 0x2732, 0xab7a, 0x37b3, 0xbbfb, 0x0630, 0x8a78, 0x16b1, 0x9af9, 0xe13e, 0x6d76, 0xf1bf, 0x7df7, 0xc03c, 0x4c74, 0xd0bd, 0x5cf5, 0xa33a, 0x2f72, 0xb3bb, 0x3ff3, 0x8238, 0x0e70, 0x92b9, 0x1ef1, 0x74bf, 0xf8f7, 0x643e, 0xe876, 0x55bd, 0xd9f5, 0x453c, 0xc974, 0x36bb, 0xbaf3, 0x263a, 0xaa72, 0x17b9, 0x9bf1, 0x0738, 0x8b70, 0xf0b7, 0x7cff, 0xe036, 0x6c7e, 0xd1b5, 0x5dfd, 0xc134, 0x4d7c, 0xb2b3, 0x3efb, 0xa232, 0x2e7a, 0x93b1, 0x1ff9, 0x8330, 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; 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 unsigned unsigned unsigned int rd; int wr; int buflen; 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);\ }\ 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; } 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_) 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:"); & (((plcMSR)(status ^ 0x80))<<16); 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; buffer<<=1; /* shift the bit to transmit to bit 0 */ /* 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)); } 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) { 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; } 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) { 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 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); 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; 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 &= ~ST7538_BAUD; reg |= bbits; writeRegister(lpmodem,®); } 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 */ 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) 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, ®))) return i; reg &= ~( ST7538_DET | ST7538_WATCHDOG | ST7538_ASYNC | ST7538_BAUD_4800); reg |= ST7538_DET_CDC | ST7538_BAUD_4800; if((i=writeRegister(lpmodem, ®))) 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 */ 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)) && wake_up_interruptible(&tty->write_wait); tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); 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; } 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; 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,®))) return i; /* This modem is lazy, if 0x0 give it a time */ msleep(100); schedule(); schedule(); if((i=readRegister(lpmodem,®1))) 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, ®))) 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; 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)); 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; 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; } } } 94 95 APÊNDICE 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 typedef typedef typedef int plcError; void *plcHandle; unsigned long plcMSR; unsigned char plcMCR; /** lpmodem driver statistics. */ struct lpmodem_statistics { unsigned long rx_packets; /**< /**< /**< /**< Error code */ Handle for a PLC device */ plcmodem MSR */ plcmodem MCR */ /**< Received packets. */ 96 unsigned unsigned unsigned unsigned long long long long tx_packets; ptt_keyed; rx_bufferoverrun; tx_bufferoverrun; /**< /**< /**< /**< Transmitted packets. */ Number of times the ptt was keyed */ Number of RX buffer overruns */ Number of TX buffer overruns */ }; /** lpmodem KISS parameters. */ struct lpmodem_params { int parport; /**< int baud; /**< int tx_delay; /**< int tx_tail; /**< int slottime; /**< int ppersist; /**< int fulldup; /**< }; Parport number to use. */ Default baud rate */ Transmitter keyup delay in 10ms units. */ Transmitter keyoff delay in 10ms units. */ Slottime in 10ms; usually 10 = 100ms. */ p-persistence 0..255. */ 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 */ enum KISS_CHARS { KISS_FEND KISS_FESC KISS_TFEND KISS_TFESC }; special characters. =0xc0, =0xdb, =0xdc, =0xdd /** KISS protocol commands. */ enum KISS_CMD { KISS_CMD_DATA KISS_CMD_TXDELAY KISS_CMD_PPERSIST KISS_CMD_SLOTTIME KISS_CMD_TXTAIL KISS_CMD_FULLDUP KISS_CMD_SETHW }; /** lpmodem ioctl commands. */ enum LPMODEM_CTL { LPMODEM_GET_DCD LPMODEM_GET_PTT LPMODEM_SET_TXDELAY LPMODEM_SET_PPERSIST LPMODEM_SET_SLOTTIME LPMODEM_SET_TXTAIL LPMODEM_SET_FULLDUP LPMODEM_GET_STAT LPMODEM_GET_PARAMS LPMODEM_SET_PARAMS LPMODEM_DEBUG_TEST #ifdef LPMODEM_DEBUG LPMODEM_GET_BITS LPMODEM_DEBUG1 LPMODEM_DEBUG2 #endif /* LPMODEM_DEBUG */ LPMODEM_GET_MSR LPMODEM_GET_REG LPMODEM_SET_REG }; #endif /* _LPMODEM_H */ /**< /**< /**< /**< =0, =1, =2, =3, =4, =5, =6, Frame End. */ Frame Escape. */ Transposed Frame End. */ Transpoded Frame Escape. */ /**< /**< /**< /**< /**< /**< /**< The rest The next The next The next The next The next Specific of the frame is data to be sent on the HDLC channel. */ byte is the transmitter keyup delay in 10 ms units. */ byte is the persistence parameter, p, scaled to the range 0 - 255. */ byte is the slot interval in 10 ms units. */ byte is the time to hold up the TX after the FCS has been sent, in 10 ms units. */ byte is 0 for half duplex, nonzero for full duplex. */ for each TNC. */ =_IOR(’L’,0,unsigned char), /**< Get the DCD state. */ =_IOR(’L’,1,unsigned char), /**< Get the ptt state. */ =_IOW(’L’,2,unsigned char), /**< Set the transmitter keyup delay in 10 ms units. */ =_IOW(’L’,3,unsigned char), /**< Set the p-persistence scaled to the range 0-255. */ =_IOW(’L’,4,unsigned char), /**< Set the slot interval in 10 ms units. */ =_IOW(’L’,5,unsigned char), /**< Set the time to hold up the TX after the FCS has been sent, in 10 ms units. */ =_IOW(’L’,6,unsigned char), /**< Set to zero for half duplex, nonzero for full duplex. */ =_IOR(’L’,7,struct lpmodem_statistics), /**< Get lpmodem statistics. */ =_IOR(’L’,8,struct lpmodem_params), /**< Set KISS lpmodem parameters. */ =_IOR(’L’,9,struct lpmodem_params), /**< Get KISS lpmodem parameters. */ =_IOR(’L’,31,st7538reg), /**< Set the transmitter keyup delay in 10 ms units. */ =_IOR(’L’,17,unsigned char), /**< Get raw HDLC frame. */ =_IOR(’L’,18,unsigned long), /**< Get the free space in RX buffer */ =_IOR(’L’,19,unsigned long), /**< Get the frenquency of interrupts */ =_IOR(’L’,20,plcMSR), /**< Get MSR register. */ =_IOR(’L’,21,st7538reg), /**< Get ST7538 Register register. */ =_IOR(’L’,22,st7538reg), /**< Set ST7538 Register register. */ 97 APÊNDICE 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, ST7538_BAUD =0x000018L, ST7538_DEV =0x000020L, ST7538_WATCHDOG =0x000040L, ST7538_TIMEOUT =0x000180L, ST7538_DET_TIME =0x000600L, ST7538_ZEROSYNC =0x000800L, ST7538_DET =0x003000L, ST7538_ASYNC =0x004000L, ST7538_CLK =0x018000L, ST7538_PACKBAUD =0x060000L, ST7538_PACKLEN =0x180000L, ST7538_PACK =0x200000L, ST7538_HISENSE =0x400000L, ST7538_FILTER =0x800000L }; /** ST7538 Channel Frequencies. */ enum ST7538_FREQ_BITS { ST7538_FREQ_60 =0x000000L, ST7538_FREQ_66 =0x000001L, ST7538_FREQ_72 =0x000002L, ST7538_FREQ_76 =0x000003L, ST7538_FREQ_82 =0x000004L, ST7538_FREQ_86 =0x000005L, ST7538_FREQ_110 =0x000006L, ST7538_FREQ_132 =0x000007L }; /** ST7538 Baud Rates. */ enum ST7538_BAUD_BITS { ST7538_BAUD_600 =0x000000L, ST7538_BAUD_1200=0x000008L, ST7538_BAUD_2400=0x000010L, ST7538_BAUD_4800=0x000018L }; /** ST7538 Time Outs. */ enum ST7538_TIMEOUT_BITS { ST7538_NO_TIMEOUT ST7538_TIMEOUT_1 ST7538_TIMEOUT_3 }; /**<channel frequency mask */ /**<baud rate mask */ /**<select deviation of 1 */ /**<enable watchdog */ /**<time out mask */ /**<frequency detection time mask */ /**<enable zero crossing synchronization */ /**<detection method mask */ /**<select asynchronous interfacing mode*/ /**<output clock mask */ /**<packet mode baud rate mask */ /**<packet lenght mask */ /**<enable packet mode */ /**<enable high sensitivity mode */ /**<enable input filter */ /**<select /**<select /**<select /**<select /**<select /**<select /**<select /**<select 60kHz channel frequency */ 60kHz channel frequency */ 72kHz channel frequency */ 76kHz channel frequency */ 82.05kHz channel frequency */ 86kHz channel frequency */ 110kHz channel frequency */ 132.5kHz channel frequency */ /**<select /**<select /**<select /**<select 600bps rate 1200bps rate 2400bps rate 4800bps rate */ */ */ */ =0x000000L, /**<no time out */ =0x000080L, /**<1s time out */ =0x000100L /**<3s time out */ /** ST7538 Frequency Detection Times. */ enum ST7538_DET_TIME_BITS { ST7538_DET_TIME_500 =0x000000L, ST7538_DET_TIME_1 =0x000200L, ST7538_DET_TIME_3 =0x000400L, ST7538_DET_TIME_5 =0x000600L }; /**<select /**<select /**<select /**<select 500us frequency detection time */ 1ms frequency detection time */ 3ms frequency detection time */ 5ms frequency detection time */ /** ST7538 Detection Methods. */ enum ST7538_DET_BITS { ST7538_DET_CD =0x000000L, /**<select carrier detection without conditioning */ 98 ST7538_DET_CDC ST7538_DET_PD ST7538_DET_PDC =0x001000L, /**<select carrier detection with conditioning */ =0x002000L, /**<select preamble detection without conditioning =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, ST7538_PACKBAUD_64 =0x020000L, ST7538_PACKBAUD_128 =0x040000L, ST7538_PACKBAUD_256 =0x060000L }; /** ST7538 Packet Lenghts. */ enum ST7538_PACKLEN_BITS { ST7538_PACKLEN_8 ST7538_PACKLEN_9 ST7538_PACKLEN_14 ST7538_PACKLEN_16 }; =0x000000L, =0x080000L, =0x100000L, =0x180000L /**<select /**<select /**<select /**<select /**<select /**<select /**<select /**<select Mclk/32 packet mode baud rate */ Mclk/64 packet mode baud rate */ Mclk/128 packet mode baud rate */ Mclk/256 packet mode baud rate */ 8 bits packet lenght */ 9 bits packet lenght */ 14 bits packet lenght */ 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; #endif /**< ST7538 register */ */