![]() |
|
![]() |
Java e USB |
|
Embora o Universal Serial Bus (USB) seja parte integrante de muitos computadores, a linguagem Java não oferece (oficialmente) suporte ao protocolo USB. Para fazer seu programa em Java interagir com um dispositivo USB qualquer, é necessário usar uma API Java/USB fornecida por terceiros ou escrever sua própria API.
Este
artigo introduz
duas APIs de terceiros e minha própria API (que possibilita
uma interação parcial com
o protocolo USB). Antes de apresentar as APIs, este artigo explora alguns fundamentos do protocolo USB.
Então é
apresentada a API JSR-80 (candidata a se tornar o suporte USB oficial
da linguagem Java) e a
API jUSB (a concorrente). Em
seguida o artigo
apresenta minha própria API, que funciona apenas no
ambiente Windows. O artigo termina ponderando se a linguagem
Java deve ou não
oferecer oficialmente
suporte
ao USB. Desenvolvi e testei os códigos que aparecem nesse artigo com o kit de desenvolvimento da Sun Microsystems J2SE5.0, e Borland C++ 5.5.1, Windows 98 SE e Windows ME foram os sistemas operacionais usados. USB em poucas palavras USB é um protocolo padrão de comunicação serial usado para conectar dispositivos em um computador, em um vídeo game, em uma televisão, etc. Esse padrão tem vantagens sobre a arquitetura usada anteriormente (placas de expansão, I/O ports, interrupções e drivers de dispositivos), para conectar dispositivos nos computadores pessoais (PCs). Essas vantagens são:
Compaq,
Intel, Microsoft e NEC começaram a trabalhar no
padrão
USB em 1994. Em Janeiro de 1996,
eles disponibilizaram a
versão
1.0. Depois de algumas revisões, foi disponibilizada a
versão
1.1 em
Setembro
de 1998. E em Abril de 2000, depois que
Hewlett-Packard, Lucent e Philips entraram no grupo,
foi distribuída
a versão 2.0.
USB
1.1 foi a primeira versão amplamente implementada do
padrão
USB. É caracterizada por duas
velocidades na transferência
dos dados (taxa de transferência): full speed (12 megabits
por
segundo) e low speed (1.5 megabits por
segundo). Essa versão
(1.1) está sendo esquecida devido ao surgimento da
versão
2.0. Na versão 2.0
é acrescentada uma terceira taxa de
transferência: high speed (480 megabits por
segundo).
Para
compreender completamente o padrão USB você
terá
de consultar a especificação USB 1.1 e a Host controller, Hubs e Funções
O
protocolo USB é implementado em termos de software (drivers,
que eu não abordo neste artigo) e
O hardware é organizado numa topologia estrela-fileira onde o centro de cada estrela é um hub: tanto o hub central quanto um hub externo estão no centro dos hubs e/ou funções nele conectados. A figura 1 esclarece esta topologia:
Figura1. Três hubs externos (hub1/hub2/hub3 ou hub1/hub2/hub4) ligados ao hub principal. Configurações, interfaces e endpoints Uma função tem uma ou mais configurações que controlam o seu comportamento. Configurações podem definir quanta energia é consumida, se o computador pode ser ativado remotamente ou não, e muito mais. Um trackball pode ser configurado como um mouse ou como um joystick, esse é um exemplo de função com múltiplas configurações. Outro exemplo são os dispositivos de comunicação RDSI (Rede de Serviços Digitais Integrada) que oferecem um canal de 128Kb ou dois canais de 56Kb. Cada configuração contém uma ou mais interfaces que especificam como o software acessa o hardware. Interfaces freqüentemente têm ajustes alternativos para corresponderem a diferentes exigências de largura de banda. Cada interface contém endpoints que podem ser a fonte ou o destino dos dados transferidos. Cada endpoint suporta transferência de dados numa única direção e tem um identificador numérico único. A figura 2 mostra a relação entre endpoints, interfaces e configurações.
Figura 2. O relacionamento entre configurações, interfaces e endpoints. Nota: os drivers da Microsoft tendem a trabalhar apenas com a primeira configuração. O resultado é que funções multi-configuráveis são raras e desencorajadas pela Microsoft. Modelo de fluxo de dados O USB suporta o fluxo de dados entre aplicações rodando no host e dispositivos físicos anexados. Esse conceito pode ser entendido observando-se o modelo de fluxo de dados USB, apresentado na figura 3:
Figura 3.
O modelo de fluxo de
dados USB dividido em três
camadas.
Na verdade a transferência dos dados se dá na camada mais baixa (camada barramento). Essa camada consiste de hubs e cabos (e conectores) e conectam o host ao dispositivo. No modelo, fisicamente, os dados fluem para cima e para baixo de ambos os lados. Virtualmente os dados fluem na horizontal, de um lado ao outro nas duas primeiras camadas através de canais lógicos (pipes) que ligam aplicativos do host aos endpoints do dispositivo. Os canais podem ser divididos em duas categorias: canais de mensagem e canais de fluxo. Canais de mensagem transferem dados com alguma estrutura USB (cada pacote de dados carrega informações significativas ao software USB), canais de fluxo transferem dados sem nenhuma estrutura USB. A camada dispositivo tem uma visão lógica e controla o dispositivo físico. Ela usa um canal de mensagem especial chamado Canal de Controle Padrão para ler a configuração dos dados de entrada endpoint () e controlar a saída dos dados para a saída endpoint() . Estes endpoints (endpoint 0) estão sempre presente. A camada de função tem uma visão da função do dispositivo físico. Esta camada usa canais de dados para captar e enviar dados para a função. Tipos de transferência Um
canal transfere dados de acordo com o tipo de transferência
do
endpoint, o tipo de transferência gerencia a quantidade de
dados que é transferida numa transação
(parte de
uma transferência), se a transferência foi perdida
ou não
e outros fatores:
Nota: ainda que eu não tenha focado a transferência de dados nesse artigo, você precisa estar a par desses tipos de transferência quando for trabalhar com as APIs JSR 80 e jUSB. Descritores Dispositivos
USB apresentam estruturas de dados conhecidas como descritores. Essas
estruturas permitem que uma função (ou
funções)
do dispositivo identifique-se (ou identifiquem-se) ao software
rodando no host:
Mais tarde nesse artigo, apresentarei um exemplo de enumeração de dispositivos onde o código em C++ acessa um dispositivo e descritores “string” durante o processo de enumeração. Classes de dispositivos Dispositivos USB pertencem à classes de dispositivos, que definem um esperado comportamento em termos de dispositivo e descritores de interface. Um driver de uma classe de dispositivos pode ser usado por qualquer dispositivo que pertença à classe. Sistemas operacionais geralmente oferecem drivers genéricos para todas as classes de dispositivos, para suportar todos os dispositivos USB. Quando um dispositivo é conectado na USB, o identificador de classe do dispositivo é lido num determinado descritor e usado para carregar o driver apropriado. A USB-IF (USB Implementers Forum) padroniza identificadores de classes de dispositivo. Como exemplo, temos: 1 (classe áudio, uma placa de som por exemplo), 3 (classe dispositivo de interface com o usuário, como teclados e mouses), 8 (classe dispositivo de armazenamento em massa, flash drivers e HDs portáveis), 9 (classe hubs). Os identificadores 0 e 255 são reservados. Se a classe pertence ao dispositivo como um todo, o identificador é atribuído ao campo de 8 bits bDeviceClass (descritor do dispositivo). Se a classe é para uma única interface o identificador é atribuído ao campo de 8 bits bInterfaceClass (é atribuído 0 (zero) ao campo bDeviceClass). JSR-80 Em 1999, Dan Streetman da IBM começou o projeto de uma API Java/USB para permitir que programas em Java interagissem com a USB. Em 2001, através da Java Community Process (JCP) esse projeto foi aceito como candidato à extensão padrão para a linguagem Java. A JCP identificou esse projeto como Java Especification Request (JSR) 80, você pode observar o estado atual da requisição JSR-80 visitando a página JSR-80 (Java USB API) no site da JCP. A página oficial de desenvolvimento com esta API é JSR80 – javax.usb. Esse site fornece links para a licença pública da JSR-80, perguntas freqüentes (FAQs), as implementações da API no compartimento de arquivos (CVS repository), e a documentação da API. O compartimento de arquivos fornece o arquivo jsr80.jar (o principal pacote da implementação javax.usb), jsr80_ri.jar (implementação de referência, independente de plataforma) e as implementações para Linux/Windows. A implementação em Linux foi certificada com o kit de testes da Sun. Você encontrará os códigos fontes em C e Java dessa implementação e o arquivo javax.usb.properties específico para Linux, no diretório javax-usb-ri-linux do compartimento de arquivos. Você terá de compilar a implementação a partir do código fonte (assumindo que seu sistema operacional seja Linux). A implementação pré-alfa para Windows não é certificada e requer um kernel driver. Os códigos fontes em C e Java dessa implementação e o arquivo javax.usb.properties específico para Windows podem ser encontrados no diretório javax-usb-ri-windows compartimento de arquivos. Se o seu sistema operacional é Windows você pode compilar a implementação a partir do código fonte (possivelmente depois de algumas alterações), mas você precisa fornecer o kernel driver. Você pôde preferir a implementação alfa para Windows. Esta implementação não certificada se localiza no diretório javax-usb-libusb do repositório. Junto com os arquivos de desenvolvimento, você terá que instalar a versão Windows da libusb. Visite a página do projeto libusb e a página do projeto libusb-win32 para conhecer e obter a versão de Windows da libsus. O documento de especificação da API JSR-80 em pdf, localizada no repositório como jsr80.pdf, descreve a arquitetura dessa API. Esse documento revela que a classe USBHostManager é o ponto inicial da API. Essa classe permite que você obtenha uma instância específica ao sistema operacional (baseada nas informações do arquivo javax.usb.properties) da interface USBServices, a partir daí é possível obter o hub principal virtual:
O hub principal virtual, obtido com o método getRootUsbHub() da classe UsbServices, permite acesso a todos os host controller disponíveis (e seus hubs principais). Os métodos public boolean isRootUsbHub(), public byte getNumberOfPorts() e public List getUsbPorts() são úteis quando se estiver verificando, enumerando e obtendo os hubs principais. Eu não entrarei muito a fundo na JSR-80, porque uma boa visão geral da API é apresentada em “Acesso a Dispositivos USB por Aplicações em Java”. Essa visão geral inclui códigos com exemplos de enumeração de dispositivos e transferência de dados, os quais demonstram a obtenção dos tipos de transferência de endpoints, direção e canais conectados. Ao invés disso, eu identifiquei quatro dicas que você precisará conhecer para instalar a JSR-80 com sucesso no seu sistema operacional:
O
diretório
lib contém também o arquivo log4j.properties,
junto dos
arquivos commons_logging.jar e
log4j.jar. Acredito que você
pode evitar ter de adicionar esses arquivos ao seu classpath
modificando o jUSB “Mojo Jojo” e David Brownel começaram o projeto de uma API Java/USB rival em junho de 2000: a jUSB. Eles desenvolveram uma versão para Linux, que está disponível na página do projeto. Para sua tese, Mike Stahl criou uma versão para Windows; visite a página do projeto jUSB: API Java/USB para Windows para obter essa versão. jUBS é distribuída sob a licença LGPL (Lesser GNU Public License). Enquanto pesquisava a jUSB, eu mantive o foco na versão Windows (de Mike Stahl). A partir da página da jUSB do Mike, eu baixei dois arquivos: JavaUSB.zip e JavaUSBComplete.zip. O primeiro arquivo contém os códigos fontes Java dos pacotes da jUSB, a dll jUSB e o kernel driver (juntamente com informações e arquivos de registro) para Windows XP e Windows 2000. O segundo arquivo inclui o código fonte da dll e do driver. Eu baixei também a tese “API Java/USB para Windows” de Mike Stahl (um pdf de 2.2 MB). Este arquivo pdf traz uma introdução a USB e revela em detalhes como Mike Stahl implementou o pacote usb.windows da jUSB para Windows XP e Windows 2000. Esse documento é leitura obrigatória para qualquer um que queira implementar uma API Java/USB que acesse a arquitetura USB do Windows. De acordo com a tese de Mike, o pacote usb.core da jUSB contém uma classe HostFactory onde o método public static Host getHost() é o ponto de entrada na jUSB. Este método retorna um objeto Host que monitora todas as árvores de dispositivos de uma máquina. Ao invés de se aprofundar na jUSB, recomendo explorar informações em “Acesso a Dispositivos USB por Aplicações em Java”. Crie sua própria API Java/USB Ainda que você possa usar as APIs JSR80 e jUSB para dar aos seus programas a capacidade de interagir com a USB, você pode optar por criar algo diferente. Para ajudar você nessa tarefa eu apresento minha própria API. Essa API é limitada à enumeração de dispositivos. Além disso, o código nativo escrito em C++ e usado pela Java Native Interface (JNI) limita a API ao Windows 98, Windows ME e (ainda que não tenha sido testado) ao Windows 2000. Construí o código C++ da API usando o compilador Borland C++ 5.5.1. Você precisará de uma cópia desse compilador (que é free) se planeja modificar o código. Forneço uma dll pré-compilada nesse artigo (veja links e downloads). Clique no link da tabela de downloads na página C++ Builder Downloads - Compilador Borland C++ 5.5.1 para baixar o compilador. No artigo "Java Tech: Aquisição de Imagens com TWAIN e SANE, Parte 1", que eu escrevi para o java.net, faz uma breve introdução do Borland C++ 5.5.1. Para uma introdução mais significativa, de uma olhada em “Familiarizando-se com o Borland C++ 5.5.1” no meu artigo “Construir Proteções de tela com a Biblioteca Padrão de Proteções de Tela no Borland C++ 5.5.1”. JavaUSB Minha API JavaUSB consiste de cinco classes (que não estão num pacote): JavaUSB, HCInfo, Device, Hub, e Function. Considere colocar essas classes em seus próprios pacotes. A classe JavaUSB serve como ponto de entrada nessa API. Ela provê três métodos para carregar e acessar as funções da dll:
Para
manter a API
simples eu não providenciei uma classe de
exceções
(exceptions) e nem fiz os métodos
nativos ativarem instancias
dessa classe caso alguma coisa saísse errado. No entanto,
esses métodos podem
disparar
erros relatados pela JNI
("classfile not found," por exemplo). Ao invés
disso, um problema é
revelado,
quando um método nativo retorna um
valor HCInfo descreve informações do host controller retornadas pelo método getHostControllerInfo(). Essa informação é armazenada em dois campos do tipo string: name contém o nome do host controller e hoothubname contém o nome do hub principal integrado ao host controller. Para começar a enumeração de uma árvore de dispositivos, passe o campo hoothubname como parâmetro para o método getAttachedDevices(). Device descreve um hub ou função anexada ao hub. Essa classe contém três campos do tipo string que são comuns tanto a hubs quanto a funções: manufacturer, identifica o fabricante do dispositivo, product, identifica o dispositivo como um produto e serialNumber, identifica o número serial do dispositivo. Esses campos podem ser nulos. Device também é uma superclasse abstrata para as classes hub e function. A classe hub apresenta um único campo string, hubName, que nomeia um hub externo. No entanto, function é uma classe vazia. No futuro, planejo expandir as classes hub e function com campos e funções que são únicos à essas classes. Para demonstrar o quanto é fácil usar os três métodos da JavaUSB e as outras classes, criei o programa TestJavaUSB. O código fonte deste aplicativo emprega quatro das cinco classes (não ha porquê usar a classe Function até que ela tenha sido mais completamente desenvolvida) para enumerar cada host controller e a árvore de dispositivos do hub principal. O código fonte do aplicativo é apresentado abaixo.
Executando o comando java TestJavaUSB no sistema operacional Windows 98 SE foi gerada a seguinte saída:
A saída abaixo apareceu quando eu executei java TestJavaUSB no sistema operacional Windows ME:
Esse código fonte levanta uma questão interessante: o que acontece se um host controller é adicionado ou removido durante a enumeração? Quando é adicionado, eu presumo que o sistema operacional vai adicionar a entrada do host controller à alguma estrutura de entradas e a enumeração não será afetada. No caso de remoção, ou o host controller já teria sido enumerado ou o método nativo retornaria null (dependendo de qual parte do código está sendo executada no momento). Atrás das cortinas A API JavaUSB funciona em Windows 98 SE e Windows ME. Apesar de não ter testado essa API no Windows 2000 (não tenho acesso a esse SO) eu acredito que ela também funcione corretamente. Se você precisar modificar o arquivo javausb.cpp para suportar outra versão do Windows (ou um outro sistema operacional) você ficará interessado nos seguintes detalhes do arquivo javausb.cpp:
Para informações sobre a construção da javausb.dll com Borlando C++ 5.5.1, examine os artigos sugeridos anteriormente. Leia também os comentários no arquivo makedll.bat (junto ao código-exemplo deste artigo). Java deveria oferecer oficialmente suporte a USB? Enquanto pesquisava para esse artigo, encontrei várias discussões sobre se Java deveria ou não oferecer oficialmente suporte a USB. Vários indivíduos querem que a Sun introduza o suporte USB no Java, outros não acham que seja uma boa idéia que Java ofereça oficialmente suporte a USB. Abaixo estão duas reações típicas:
Se o Java oficializar o suporte a USB, esse suporte deveria estar presente em todos os SOs que suportam Java (alguns Sos ainda não oferecem suporte a USB). Acima de tudo, Java é uma tecnologia independente de plataforma. Mas o que acontece se um SO que suporta Java suportar FireWire (ou alguma outra tecnologia) ao invés de USB? Alguns desenvolvedores tem expressado o desejo de ver APIs na linguagem Java dando suporte oficial a FireWire e BlueTooth (que eu imagino ser uma espécie de USB sem fio) além da USB. Embora a API JSR-82 (API Java para BlueTooth) esteja funcionando (aparentemente não existe API Java para FireWire) faz sentido manter separadas as APIs USB e BlueTooth ? USB, FireWire e BlueTooth são três tecnologias de comunicação com dispositivos. Java tem uma longa história de abstração sobre tecnologias similares, talvez seja possível planejar uma única API serial que abstraia essas e outras tecnologias similares que estão por vir. Talvez essa deveria ser a maneira de oferecer oficialmente suporte a essas tecnologias. Conclusão Java, oficialmente, não oferece suporte a USB, portanto você precisa trabalhar com a JSR-80, jUSB ou alguma outra API Java/USB de terceiros (ou criar sua própria API) para permitir que seus programas Java interajam com a USB. Se você planeja criar sua própria API no ambiente Windows, a API JavaUSB pode ajudar você com a parte de enumeração de dispositivos. Deliberadamente evitei o suporte à transferência de dados por duas razões:
Meu trabalho com JavaUSB tem mostrado que não é fácil desenvolver uma API Java/USB. Essa API tem que ser útil e não deve expor os detalhes de baixo-nível do hardware. Tem também que ser abstrata o suficiente para se tornar portável. Porque escolhi como alvo os sistemas operacionais Windows 98 SE/ME/2000, deixo você com a seguinte tarefa: tente passar a JavaUSB para o Linux ou algum outro SO. Links e Downloads (Obs: todo o material em inglês)
Jeff Friesen é um desenvolvedor de software freelance e educador, especializando-se na tecnologia Java. Verifique seu site javajeff.mb.ca. |