ChangeSet 1.1608.84.10, 2004/03/09 14:12:08-08:00, s.doyon@videotron.ca [PATCH] USB brlvger: Driver obsoleted by rewrite using usbfs We have rewritten the brlvger (Tieman Voyager USB Braille display) driver so that it works from user-space through usbfs. It appears to work just as well as the in-kernel driver. The brlvger driver in the 2.6.x kernel is now obsolete and should be removed. The attached patch against 2.6.3 does this. Please apply. NB: The following files are completely deleted: Documentation/usb/brlvger.txt drivers/usb/misc/brlvger.c include/linux/brlvger.h The new Voyager driver is available (stil under GPL) as part of BRLTTY, starting with version 3.5pre1 (http://mielke.cc/brltty). Thanks to Dave Mielke who implemented BRLTTY's usbfs functionality, among lots of other stuff. Documentation/usb/brlvger.txt | 36 - drivers/usb/misc/brlvger.c | 1016 ------------------------------------------ include/linux/brlvger.h | 54 -- CREDITS | 10 MAINTAINERS | 7 drivers/usb/Makefile | 1 drivers/usb/misc/Kconfig | 11 drivers/usb/misc/Makefile | 1 8 files changed, 1136 deletions(-) diff -Nru a/CREDITS b/CREDITS --- a/CREDITS Tue Mar 16 15:03:20 2004 +++ b/CREDITS Tue Mar 16 15:03:20 2004 @@ -673,11 +673,6 @@ S: NN1 3QT S: United Kingdom -N: Stephane Dalton -E: sdalton@videotron.ca -D: Tieman Voyager USB Braille display driver. -S: Québec, Canada - N: Uwe Dannowski E: Uwe.Dannowski@ira.uka.de W: http://i30www.ira.uka.de/~dannowsk/ @@ -796,11 +791,6 @@ E: cort@fsmlabs.com W: http://www.fsmlabs.com/linuxppcbk.html D: PowerPC - -N: Stéphane Doyon -E: s.doyon@videotron.ca -D: Tieman Voyager USB Braille display driver. -S: Québec, Canada N: Oleg Drokin E: green@ccssu.crimea.ua diff -Nru a/Documentation/usb/brlvger.txt b/Documentation/usb/brlvger.txt --- a/Documentation/usb/brlvger.txt Tue Mar 16 15:03:20 2004 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,36 +0,0 @@ -Kernel Driver for the Tieman Voyager Braille Display (USB) - -Authors: -Stéphane Dalton -Stéphane Doyon - -Version 0.8, April 17, 2002 - -The brlvger driver supports a Braille display (aka Braille terminal) -model Voyager from Tieman. - -The driver has been in heavy use for about six months now (as of April -17th 2002) by a very few users (about 3-4), who say it has worked very -well for them. - -We have tested it with a Voyager 44, but it should also support -the Voyager 70. - -This driver implements a character device which allows userspace programs -access to the braille displays raw functions. You still need a userspace -program to perform the screen-review functions and control the -display. Get BRLTTY from http://mielke.cc/brltty/ (version 2.99.8 or -later). It has a Voyager driver which interfaces with this kernel driver. - -The interface is through a character device, major 180, minor 128, called -"brlvger" under devfs. - -Many thanks to the Tieman people: Corand van Strien, Ivar Illing, Daphne -Vogelaar and Ingrid Vogel. They provided us with a Braille display (as -well as programming information) so that we could write this driver. They -replaced the display when it broke and they answered our technical -questions. It is very motivating when companies take an interest in such -projects and are so supportive. - -Thanks to Andor Demarteau who got this whole -project started and beta-tested all our early buggy attempts. diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Tue Mar 16 15:03:20 2004 +++ b/MAINTAINERS Tue Mar 16 15:03:20 2004 @@ -1966,13 +1966,6 @@ M: roms@lpg.ticalc.org S: Maintained -TIEMAN VOYAGER USB BRAILLE DISPLAY DRIVER -P: Stephane Dalton -M: sdalton@videotron.ca -P: Stéphane Doyon -M: s.doyon@videotron.ca -S: Maintained - TLAN NETWORK DRIVER P: Samuel Chessman M: chessman@tux.org diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Tue Mar 16 15:03:20 2004 +++ b/drivers/usb/Makefile Tue Mar 16 15:03:20 2004 @@ -55,7 +55,6 @@ obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB_AUERSWALD) += misc/ -obj-$(CONFIG_USB_BRLVGER) += misc/ obj-$(CONFIG_USB_EMI26) += misc/ obj-$(CONFIG_USB_LCD) += misc/ obj-$(CONFIG_USB_LEGOTOWER) += misc/ diff -Nru a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig --- a/drivers/usb/misc/Kconfig Tue Mar 16 15:03:20 2004 +++ b/drivers/usb/misc/Kconfig Tue Mar 16 15:03:20 2004 @@ -86,17 +86,6 @@ a module, say M here and read . -config USB_BRLVGER - tristate "Tieman Voyager USB Braille display support (EXPERIMENTAL)" - depends on USB && EXPERIMENTAL - help - Say Y here if you want to use the Voyager USB Braille display from - Tieman. See for more - information. - - To compile this driver as a module, choose M here: the - module will be called brlvger. - config USB_LCD tristate "USB LCD driver support" depends on USB diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile --- a/drivers/usb/misc/Makefile Tue Mar 16 15:03:20 2004 +++ b/drivers/usb/misc/Makefile Tue Mar 16 15:03:20 2004 @@ -4,7 +4,6 @@ # obj-$(CONFIG_USB_AUERSWALD) += auerswald.o -obj-$(CONFIG_USB_BRLVGER) += brlvger.o obj-$(CONFIG_USB_EMI62) += emi62.o obj-$(CONFIG_USB_EMI26) += emi26.o obj-$(CONFIG_USB_LCD) += usblcd.o diff -Nru a/drivers/usb/misc/brlvger.c b/drivers/usb/misc/brlvger.c --- a/drivers/usb/misc/brlvger.c Tue Mar 16 15:03:20 2004 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1016 +0,0 @@ -/* - * Tieman Voyager braille display USB driver. - * - * Copyright 2001-2002 Stephane Dalton - * and Stéphane Doyon - * Maintained by Stéphane Doyon . - */ -/* - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -/* History: - * 0.8 April 2002: Integration into the kernel tree. - * 0.7 October 2001: First public release as a module, distributed with - * the BRLTTY package (beta versions around 2.99y). - */ - -#define DRIVER_VERSION "v0.8" -#define DATE "April 2002" -#define DRIVER_AUTHOR \ - "Stephane Dalton " \ - "and Stéphane Doyon " -#define DRIVER_DESC "Tieman Voyager braille display USB driver for Linux 2.4" -#define DRIVER_SHORTDESC "Voyager" - -#define BANNER \ - KERN_INFO DRIVER_SHORTDESC " " DRIVER_VERSION " (" DATE ")\n" \ - KERN_INFO " by " DRIVER_AUTHOR "\n" - -static const char longbanner[] = { - DRIVER_DESC ", " DRIVER_VERSION " (" DATE "), by " DRIVER_AUTHOR -}; - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); - -/* Module parameters */ - -static int debug = 1; -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "Debug level, 0-3"); - -static int write_repeats = 2; -MODULE_PARM(write_repeats, "i"); -MODULE_PARM_DESC(write_repeats, "Hack: repetitions for command to " - "display braille pattern"); - /* to get rid of weird extra dots (perhaps only on - early hardware versions?) */ - -static int stall_tries = 3; -MODULE_PARM(stall_tries, "i"); -MODULE_PARM_DESC(stall_tries, "Hack: retransmits of stalled USB " - "control messages"); - /* broken early hardware versions? */ - -#define BRLVGER_RAW_VOLTAGE 89 -/* from 0->300V to 255->200V, we are told 265V is normal operating voltage, - but we don't know the scale. Assuming it is linear. */ -static int raw_voltage = BRLVGER_RAW_VOLTAGE; -MODULE_PARM(raw_voltage, "i"); -MODULE_PARM_DESC(raw_voltage, "Parameter for the call to SET_DISPLAY_VOLTAGE"); - - -/* protocol and display type defines */ -#define MAX_BRLVGER_CELLS 72 -#define MAX_INTERRUPT_DATA 8 -/* control message request types */ -#define BRLVGER_READ_REQ 0xC2 -#define BRLVGER_WRITE_REQ 0x42 -/* control message request codes */ -#define BRLVGER_SET_DISPLAY_ON 0 -#define BRLVGER_SET_DISPLAY_VOLTAGE 1 -#define BRLVGER_GET_SERIAL 3 -#define BRLVGER_GET_HWVERSION 4 -#define BRLVGER_GET_FWVERSION 5 -#define BRLVGER_GET_LENGTH 6 -#define BRLVGER_SEND_BRAILLE 7 -#define BRLVGER_BEEP 9 -#if 0 /* not used and not sure they're working */ -#define BRLVGER_GET_DISPLAY_VOLTAGE 2 -#define BRLVGER_GET_CURRENT 8 -#endif - -/* Prototypes */ -static int brlvger_probe (struct usb_interface *intf, - const struct usb_device_id *id); -static void brlvger_disconnect(struct usb_interface *intf); -static int brlvger_open(struct inode *inode, struct file *file); -static int brlvger_release(struct inode *inode, struct file *file); -static ssize_t brlvger_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos); -static ssize_t brlvger_read(struct file *file, char __user *buffer, - size_t count, loff_t *unused_pos); -static int brlvger_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg); -static unsigned brlvger_poll(struct file *file, poll_table *wait); -static loff_t brlvger_llseek(struct file * file, loff_t offset, int orig); -static void intr_callback(struct urb *urb, struct pt_regs *regs); -struct brlvger_priv; -static int brlvger_get_hw_version(struct brlvger_priv *priv, - unsigned char *verbuf); -static int brlvger_get_fw_version(struct brlvger_priv *priv, - unsigned char *buf); -static int brlvger_get_serial(struct brlvger_priv *priv, - unsigned char *buf); -static int brlvger_get_display_length(struct brlvger_priv *priv); -static int brlvger_set_display_on_off(struct brlvger_priv *priv, __u16 on); -static int brlvger_beep(struct brlvger_priv *priv, __u16 duration); -static int brlvger_set_display_voltage(struct brlvger_priv *priv, - __u16 voltage); -static int mycontrolmsg(const char *funcname, - struct brlvger_priv *priv, unsigned pipe_dir, - __u8 request, __u8 requesttype, __u16 value, - __u16 index, void *data, __u16 size); - -#define controlmsg(priv,pipe_dir,a,b,c,d,e,f) \ - mycontrolmsg(__FUNCTION__, priv, pipe_dir, \ - a,b,c,d,e,f) -#define sndcontrolmsg(priv,a,b,c,d,e,f) \ - controlmsg(priv, 0, a,b,c,d,e,f) -#define rcvcontrolmsg(priv,a,b,c,d,e,f) \ - controlmsg(priv, USB_DIR_IN, a,b,c,d,e,f) - -/* ----------------------------------------------------------------------- */ - -/* Data */ - -/* key event queue size */ -#define MAX_INTERRUPT_BUFFER 10 - -/* private state */ -struct brlvger_priv { - struct usb_device *dev; /* USB device handle */ - struct usb_endpoint_descriptor *in_interrupt; - struct urb *intr_urb; - - int subminor; /* which minor dev #? */ - - unsigned char hwver[BRLVGER_HWVER_SIZE]; /* hardware version */ - unsigned char fwver[BRLVGER_FWVER_SIZE]; /* firmware version */ - unsigned char serialnum[BRLVGER_SERIAL_SIZE]; - - int llength; /* logical length */ - int plength; /* physical length */ - - __u8 obuf[MAX_BRLVGER_CELLS]; - __u8 intr_buff[MAX_INTERRUPT_DATA]; - __u8 event_queue[MAX_INTERRUPT_BUFFER][MAX_INTERRUPT_DATA]; - atomic_t intr_idx, read_idx; - spinlock_t intr_idx_lock; /* protects intr_idx */ - wait_queue_head_t read_wait; - - int opened; - struct semaphore open_sem; /* protects ->opened */ - struct semaphore dev_sem; /* protects ->dev */ -}; - -/* Globals */ - -/* For blocking open */ -static DECLARE_WAIT_QUEUE_HEAD(open_wait); - -/* Some print macros */ -#ifdef dbg -#undef dbg -#endif -#ifdef info -#undef info -#endif -#ifdef err -#undef err -#endif -#define info(args...) \ - ({ printk(KERN_INFO "Voyager: " args); \ - printk("\n"); }) -#define err(args...) \ - ({ printk(KERN_ERR "Voyager: " args); \ - printk("\n"); }) -#define dbgprint(fmt, args...) \ - ({ printk(KERN_DEBUG "Voyager: %s: " fmt, __FUNCTION__ , ##args); \ - printk("\n"); }) -#define dbg(args...) \ - ({ if(debug >= 1) dbgprint(args); }) -#define dbg2(args...) \ - ({ if(debug >= 2) dbgprint(args); }) -#define dbg3(args...) \ - ({ if(debug >= 3) dbgprint(args); }) - -/* ----------------------------------------------------------------------- */ - -/* Driver registration */ - -static struct usb_device_id brlvger_ids [] = { - { USB_DEVICE(0x0798, 0x0001) }, - { } /* Terminating entry */ -}; -MODULE_DEVICE_TABLE (usb, brlvger_ids); - -static struct file_operations brlvger_fops = -{ - .owner = THIS_MODULE, - .llseek = brlvger_llseek, - .read = brlvger_read, - .write = brlvger_write, - .ioctl = brlvger_ioctl, - .open = brlvger_open, - .release = brlvger_release, - .poll = brlvger_poll, -}; - -static struct usb_class_driver brlvger_class = { - .name = "usb/brlvger%d", - .fops = &brlvger_fops, - .mode = S_IFCHR | S_IRUSR |S_IWUSR | S_IRGRP | S_IWGRP, - .minor_base = BRLVGER_MINOR, -}; - -static struct usb_driver brlvger_driver = -{ - .owner = THIS_MODULE, - .name = "brlvger", - .probe = brlvger_probe, - .disconnect = brlvger_disconnect, - .id_table = brlvger_ids, -}; - -static int -__init brlvger_init (void) -{ - int retval; - printk(BANNER); - - if(stall_tries < 1 || write_repeats < 1) - return -EINVAL; - - retval = usb_register(&brlvger_driver); - if (retval) { - err("USB registration failed"); - goto out; - } - -out: - return retval; -} - -static void -__exit brlvger_cleanup (void) -{ - usb_deregister (&brlvger_driver); - dbg("Driver unregistered"); -} - -module_init (brlvger_init); -module_exit (brlvger_cleanup); - -/* ----------------------------------------------------------------------- */ - -/* Probe and disconnect functions */ - -static int -brlvger_probe (struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct brlvger_priv *priv = NULL; - int retval; - struct usb_endpoint_descriptor *endpoint; - struct usb_host_interface *actifsettings; - /* protects against reentrance: once we've found a free slot - we reserve it.*/ - static DECLARE_MUTEX(reserve_sem); - - actifsettings = dev->actconfig->interface[0]->altsetting; - - if( dev->descriptor.bNumConfigurations != 1 - || dev->config->desc.bNumInterfaces != 1 - || actifsettings->desc.bNumEndpoints != 1 ) { - err ("Bogus braille display config info"); - return -ENODEV; - } - - endpoint = &actifsettings->endpoint [0].desc; - if (!(endpoint->bEndpointAddress & 0x80) || - ((endpoint->bmAttributes & 3) != 0x03)) { - err ("Bogus braille display config info, wrong endpoints"); - return -ENODEV; - } - - down(&reserve_sem); - - retval = usb_register_dev(intf, &brlvger_class); - if (retval) { - err("Not able to get a minor for this device."); - goto error; - } - - if( !(priv = kmalloc (sizeof *priv, GFP_KERNEL)) ){ - err("No more memory"); - goto error; - } - - memset(priv, 0, sizeof(*priv)); - atomic_set(&priv->intr_idx, 0); - atomic_set(&priv->read_idx, MAX_INTERRUPT_BUFFER-1); - spin_lock_init(&priv->intr_idx_lock); - init_waitqueue_head(&priv->read_wait); - /* opened is memset'ed to 0 */ - init_MUTEX(&priv->open_sem); - init_MUTEX(&priv->dev_sem); - - priv->subminor = intf->minor; - - /* we found a interrupt in endpoint */ - priv->in_interrupt = endpoint; - - priv->dev = dev; - - if(brlvger_get_hw_version(priv, priv->hwver) <0) { - err("Unable to get hardware version"); - goto error; - } - dbg("Hw ver %d.%d", priv->hwver[0], priv->hwver[1]); - if(brlvger_get_fw_version(priv, priv->fwver) <0) { - err("Unable to get firmware version"); - goto error; - } - dbg("Fw ver: %s", priv->fwver); - - if(brlvger_get_serial(priv, priv->serialnum) <0) { - err("Unable to get serial number"); - goto error; - } - dbg("Serial number: %s", priv->serialnum); - - if( (priv->llength = brlvger_get_display_length(priv)) <0 ){ - err("Unable to get display length"); - goto error; - } - switch(priv->llength) { - case 48: - priv->plength = 44; - break; - case 72: - priv->plength = 70; - break; - default: - err("Unsupported display length: %d", priv->llength); - goto error; - }; - dbg("Display length: %d", priv->plength); - - usb_set_intfdata (intf, priv); - info( "Braille display %d is device major %d minor %d", - intf->minor, USB_MAJOR, BRLVGER_MINOR + intf->minor); - - /* Tell anyone waiting on a blocking open */ - wake_up_interruptible(&open_wait); - - goto out; - - error: - if(priv) { - kfree( priv ); - priv = NULL; - } - - out: - up(&reserve_sem); - if (priv) { - usb_set_intfdata (intf, priv); - return 0; - } - return -EIO; -} - -static void -brlvger_disconnect(struct usb_interface *intf) -{ - struct brlvger_priv *priv = usb_get_intfdata (intf); - int r; - - usb_set_intfdata (intf, NULL); - if(priv){ - info("Display %d disconnecting", priv->subminor); - - usb_deregister_dev(intf, &brlvger_class); - - down(&priv->open_sem); - down(&priv->dev_sem); - if(priv->opened) { - /* Disable interrupts */ - if((r = usb_unlink_urb(priv->intr_urb)) <0) - err("usb_unlink_urb returns %d", r); - usb_free_urb(priv->intr_urb); - /* mark device as dead and prevent control - messages to it */ - priv->dev = NULL; - /* Tell anyone hung up on a read that it - won't be coming */ - wake_up_interruptible(&priv->read_wait); - up(&priv->dev_sem); - up(&priv->open_sem); - }else - /* no corresponding up()s */ - kfree(priv); - } -} - -/* ----------------------------------------------------------------------- */ - -/* fops implementation */ - -static int -brlvger_open(struct inode *inode, struct file *file) -{ - int devnum = iminor(inode); - struct usb_interface *intf = NULL; - struct brlvger_priv *priv = NULL; - int n, ret; - - if (devnum < 0) - return -ENXIO; - - n = devnum - BRLVGER_MINOR; - - do { - intf = usb_find_interface(&brlvger_driver, devnum); - if (!intf) { - if (file->f_flags & O_NONBLOCK) { - dbg3("Failing non-blocking open: " - "device %d not connected", n); - return -EAGAIN; - } - /* Blocking open. One global wait queue will - suffice. We wait until a device for the selected - minor is connected. */ - dbg2("Waiting for device %d to be connected", n); - ret = wait_event_interruptible(open_wait, - (intf = usb_find_interface(&brlvger_driver, devnum))); - if (ret) { - dbg2("Interrupted wait for device %d", n); - return ret; - } - } - } while(!intf); - priv = usb_get_intfdata(intf); - - /* We grabbed an existing device. */ - if(down_interruptible(&priv->open_sem)) - return -ERESTARTSYS; - - /* Only one process can open each device, no sharing. */ - ret = -EBUSY; - if(priv->opened) - goto out; - - dbg("Opening display %d", priv->subminor); - - /* Setup interrupt handler for receiving key input */ - priv->intr_urb = usb_alloc_urb(0, GFP_KERNEL); - if(!priv->intr_urb) { - err("Unable to allocate URB"); - goto out; - } - usb_fill_int_urb( priv->intr_urb, priv->dev, - usb_rcvintpipe(priv->dev, - priv->in_interrupt->bEndpointAddress), - priv->intr_buff, sizeof(priv->intr_buff), - intr_callback, priv, priv->in_interrupt->bInterval); - if((ret = usb_submit_urb(priv->intr_urb, GFP_KERNEL)) <0){ - err("Error %d while submitting URB", ret); - goto out; - } - - /* Set voltage */ - if(brlvger_set_display_voltage(priv, raw_voltage) <0) { - err("Unable to set voltage"); - goto out; - } - - /* Turn display on */ - if((ret = brlvger_set_display_on_off(priv, 1)) <0) { - err("Error %d while turning display on", ret); - goto out; - } - - /* Mark as opened, so disconnect cannot free priv. */ - priv->opened = 1; - - file->private_data = priv; - - ret = 0; - goto out; - - out: - up(&priv->open_sem); - return ret; -} - -static int -brlvger_release(struct inode *inode, struct file *file) -{ - struct brlvger_priv *priv = file->private_data; - int r; - - /* Turn display off. Safe even if disconnected. */ - brlvger_set_display_on_off(priv, 0); - - /* mutex with disconnect and with open */ - down(&priv->open_sem); - - if(!priv->dev) { - dbg("Releasing disconnected device %d", priv->subminor); - /* no up(&priv->open_sem) */ - kfree(priv); - }else{ - dbg("Closing display %d", priv->subminor); - /* Disable interrupts */ - if((r = usb_unlink_urb(priv->intr_urb)) <0) - err("usb_unlink_urb returns %d", r); - usb_free_urb(priv->intr_urb); - priv->opened = 0; - up(&priv->open_sem); - } - - return 0; -} - -static ssize_t -brlvger_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - struct brlvger_priv *priv = file->private_data; - char buf[MAX_BRLVGER_CELLS]; - int ret; - size_t rs; - loff_t off; - __u16 written; - - if(!priv->dev) - return -ENOLINK; - - off = *pos; - - if(off > priv->plength) - return -ESPIPE;; - - rs = priv->plength - off; - - if(count > rs) - count = rs; - written = count; - - if (copy_from_user (buf, buffer, count ) ) - return -EFAULT; - - memset(priv->obuf, 0xaa, sizeof(priv->obuf)); - - /* Firmware supports multiples of 8cells, so some cells are absent - and for some reason there actually are holes! euurkkk! */ - - if( priv->plength == 44 ) { - /* Two ghost cells at the beginning of the display, plus - two more after the sixth physical cell. */ - if(off > 5) { - off +=4; - memcpy(priv->obuf, buf, count); - }else{ - int firstpart = 6 - off; - -#ifdef WRITE_DEBUG - dbg3("off: %lld, rs: %d, count: %d, firstpart: %d", - off, rs, count, firstpart); -#endif - - firstpart = (firstpart < count) ? firstpart : count; - -#ifdef WRITE_DEBUG - dbg3("off: %lld", off); - dbg3("firstpart: %d", firstpart); -#endif - - memcpy(priv->obuf, buf, firstpart); - - if(firstpart != count) { - int secondpart = count - firstpart; -#ifdef WRITE_DEBUG - dbg3("secondpart: %d", secondpart); -#endif - - memcpy(priv->obuf+(firstpart+2), - buf+firstpart, secondpart); - written +=2; - } - - off +=2; - -#ifdef WRITE_DEBUG - dbg3("off: %lld, rs: %d, count: %d, firstpart: %d, " - "written: %d", off, rs, count, firstpart, written); -#endif - } - }else{ - /* Two ghost cells at the beginningg of the display. */ - memcpy(priv->obuf, buf, count); - off += 2; - } - - { - int repeat = write_repeats; - /* Dirty hack: sometimes some of the dots are wrong and somehow - right themselves if the command is repeated. */ - while(repeat--) { - ret = sndcontrolmsg(priv, - BRLVGER_SEND_BRAILLE, BRLVGER_WRITE_REQ, 0, - off, priv->obuf, written); - if(ret <0) - return ret; - } - } - - return count; -} - -static int -read_index(struct brlvger_priv *priv) -{ - int intr_idx, read_idx; - - read_idx = atomic_read(&priv->read_idx); - read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx; - - intr_idx = atomic_read(&priv->intr_idx); - - return(read_idx == intr_idx ? -1 : read_idx); -} - -static ssize_t -brlvger_read(struct file *file, char __user *buffer, - size_t count, loff_t *unused_pos) -{ - struct brlvger_priv *priv = file->private_data; - int read_idx; - - if(count != MAX_INTERRUPT_DATA) - return -EINVAL; - - if(!priv->dev) - return -ENOLINK; - - if((read_idx = read_index(priv)) == -1) { - /* queue empty */ - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - else{ - int r = wait_event_interruptible(priv->read_wait, - (!priv->dev || (read_idx = read_index(priv)) != -1)); - if(!priv->dev) - return -ENOLINK; - if(r) - return r; - if(read_idx == -1) - /* should not happen */ - return 0; - } - } - - if (copy_to_user (buffer, priv->event_queue[read_idx], count) ) - return( -EFAULT); - - atomic_set(&priv->read_idx, read_idx); - /* Multiple opens are not allowed. Yet on SMP, two processes could - read at the same time (on a shared file descriptor); then it is not - deterministic whether or not they will get duplicates of a key - event. */ - return MAX_INTERRUPT_DATA; -} - -static int -brlvger_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg) -{ - struct brlvger_priv *priv = file->private_data; - - if(!priv->dev) - return -ENOLINK; - - switch(cmd) { - case BRLVGER_GET_INFO: { - struct brlvger_info vi; - - memset(&vi, 0, sizeof(vi)); - strlcpy(vi.driver_version, DRIVER_VERSION, - sizeof(vi.driver_version)); - strlcpy(vi.driver_banner, longbanner, - sizeof(vi.driver_banner)); - - vi.display_length = priv->plength; - - memcpy(&vi.hwver, priv->hwver, BRLVGER_HWVER_SIZE); - memcpy(&vi.fwver, priv->fwver, BRLVGER_FWVER_SIZE); - memcpy(&vi.serialnum, priv->serialnum, BRLVGER_SERIAL_SIZE); - - if(copy_to_user((void __user *)arg, &vi, sizeof(vi))) - return -EFAULT; - return 0; - } - case BRLVGER_DISPLAY_ON: - return brlvger_set_display_on_off(priv, 1); - case BRLVGER_DISPLAY_OFF: - return brlvger_set_display_on_off(priv, 0); - case BRLVGER_BUZZ: { - __u16 duration; - if(get_user(duration, (__u16 *)arg)) - return -EFAULT; - return brlvger_beep(priv, duration); - } - -#if 0 /* Underlying commands don't seem to work for some reason; not clear if - we'd want to export these anyway. */ - case BRLVGER_SET_VOLTAGE: { - __u16 voltage; - if(get_user(voltage, (__u16 *)arg)) - return -EFAULT; - return brlvger_set_display_voltage(priv, voltage); - } - case BRLVGER_GET_VOLTAGE: { - __u8 voltage; - int r = brlvger_get_display_voltage(priv); - if(r <0) - return r; - voltage = r; - if(put_user(voltage, (__u8 *)arg)) - return -EFAULT; - return 0; - } -#endif - default: - return -EINVAL; - }; -} - -static loff_t -brlvger_llseek(struct file *file, loff_t offset, int orig) -{ - struct brlvger_priv *priv = file->private_data; - - if(!priv->dev) - return -ENOLINK; - - switch (orig) { - case 0: - /* nothing to do */ - break; - case 1: - offset +=file->f_pos; - break; - case 2: - offset += priv->plength; - default: - return -EINVAL; - } - - if((offset >= priv->plength) || (offset < 0)) - return -EINVAL; - - return (file->f_pos = offset); -} - -static unsigned -brlvger_poll(struct file *file, poll_table *wait) -{ - struct brlvger_priv *priv = file->private_data; - - if(!priv->dev) - return POLLERR | POLLHUP; - - poll_wait(file, &priv->read_wait, wait); - - if(!priv->dev) - return POLLERR | POLLHUP; - if(read_index(priv) != -1) - return POLLIN | POLLRDNORM; - - return 0; -} - -static void -intr_callback(struct urb *urb, struct pt_regs *regs) -{ - struct brlvger_priv *priv = urb->context; - int intr_idx, read_idx; - int status; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); - goto exit; - } - - read_idx = atomic_read(&priv->read_idx); - spin_lock(&priv->intr_idx_lock); - intr_idx = atomic_read(&priv->intr_idx); - if(read_idx == intr_idx) { - dbg2("Queue full, dropping braille display input"); - spin_unlock(&priv->intr_idx_lock); - goto exit; /* queue full */ - } - - memcpy(priv->event_queue[intr_idx], urb->transfer_buffer, - MAX_INTERRUPT_DATA); - - intr_idx = (++intr_idx == MAX_INTERRUPT_BUFFER)? 0 : intr_idx; - atomic_set(&priv->intr_idx, intr_idx); - spin_unlock(&priv->intr_idx_lock); - - wake_up_interruptible(&priv->read_wait); - -exit: - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status) - err ("%s - usb_submit_urb failed with result %d", - __FUNCTION__, status); -} - -/* ----------------------------------------------------------------------- */ - -/* Hardware access functions */ - -static int -mycontrolmsg(const char *funcname, - struct brlvger_priv *priv, unsigned pipe_dir, - __u8 request, __u8 requesttype, __u16 value, - __u16 index, void *data, __u16 size) -{ - int ret=0, tries = stall_tries; - - /* Make sure the device was not disconnected */ - if(down_interruptible(&priv->dev_sem)) - return -ERESTARTSYS; - if(!priv->dev) { - up(&priv->dev_sem); - return -ENOLINK; - } - - /* Dirty hack for retransmission: stalls and fails all the time - without this on the hardware we tested. */ - while(tries--) { - ret = usb_control_msg(priv->dev, - usb_sndctrlpipe(priv->dev,0) |pipe_dir, - request, requesttype, value, - index, data, size, - HZ); - if(ret != -EPIPE) - break; - dbg2("Stalled, remaining %d tries", tries); - } - up(&priv->dev_sem); - if(ret <0) { - err("%s: usb_control_msg returns %d", - funcname, ret); - return -EIO; - } - return 0; -} - -static int -brlvger_get_hw_version(struct brlvger_priv *priv, unsigned char *verbuf) -{ - return rcvcontrolmsg(priv, - BRLVGER_GET_HWVERSION, BRLVGER_READ_REQ, 0, - 0, verbuf, BRLVGER_HWVER_SIZE); - /* verbuf should be 2 bytes */ -} - -static int -brlvger_get_fw_version(struct brlvger_priv *priv, unsigned char *buf) -{ - unsigned char rawbuf[(BRLVGER_FWVER_SIZE-1)*2+2]; - int i, len; - int r = rcvcontrolmsg(priv, - BRLVGER_GET_FWVERSION, BRLVGER_READ_REQ, 0, - 0, rawbuf, sizeof(rawbuf)); - if(r<0) - return r; - - /* If I guess correctly: succession of 16bit words, the string is - formed of the first byte of each of these words. First byte in - buffer indicates total length of data; not sure what second byte is - for. */ - len = rawbuf[0]-2; - if(len<0) - len = 0; - else if(len+1 > BRLVGER_FWVER_SIZE) - len = BRLVGER_FWVER_SIZE-1; - for(i=0; i9) ? (n)+'A' : (n)+'0') - buf[2*i] = NUM_TO_HEX(rawserial[i] >>4); - buf[2*i+1] = NUM_TO_HEX(rawserial[i] &0xf); - } - buf[2*i] = 0; - return 0; -} - -static int -brlvger_get_display_length(struct brlvger_priv *priv) -{ - unsigned char data[2]; - int ret = rcvcontrolmsg(priv, - BRLVGER_GET_LENGTH, BRLVGER_READ_REQ, 0, - 0, data, 2); - if(ret<0) - return ret; - return data[1]; -} - -static int -brlvger_beep(struct brlvger_priv *priv, __u16 duration) -{ - return sndcontrolmsg(priv, - BRLVGER_BEEP, BRLVGER_WRITE_REQ, duration, - 0, NULL, 0); -} - -static int -brlvger_set_display_on_off(struct brlvger_priv *priv, __u16 on) -{ - dbg2("Turning display %s", ((on) ? "on" : "off")); - return sndcontrolmsg(priv, - BRLVGER_SET_DISPLAY_ON, BRLVGER_WRITE_REQ, on, - 0, NULL, 0); -} - -static int -brlvger_set_display_voltage(struct brlvger_priv *priv, __u16 voltage) -{ - dbg("SET_DISPLAY_VOLTAGE to %u", voltage); - return sndcontrolmsg(priv, - BRLVGER_SET_DISPLAY_VOLTAGE, BRLVGER_WRITE_REQ, voltage, - 0, NULL, 0); -} - -#if 0 /* Had problems testing these commands. Not particularly useful anyway.*/ - -static int -brlvger_get_display_voltage(struct brlvger_priv *priv) -{ - __u8 voltage = 0; - int ret = rcvcontrolmsg(priv, - BRLVGER_GET_DISPLAY_VOLTAGE, BRLVGER_READ_REQ, 0, - 0, &voltage, 1); - if(ret<0) - return ret; - return voltage; -} - -static int -brlvger_get_current(struct brlvger_priv *priv) -{ - unsigned char data; - int ret = rcvcontrolmsg(priv, - BRLVGER_GET_CURRENT, BRLVGER_READ_REQ, 0, - 0, &data, 1); - if(ret<0) - return ret; - return data; -} -#endif diff -Nru a/include/linux/brlvger.h b/include/linux/brlvger.h --- a/include/linux/brlvger.h Tue Mar 16 15:03:20 2004 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,54 +0,0 @@ -/* - * Tieman Voyager braille display USB driver. - * - * Copyright 2001-2002 Stephane Dalton - * and Stéphane Doyon - * Maintained by Stéphane Doyon . - */ -/* - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _LINUX_BRLVGER_H -#define _LINUX_BRLVGER_H - -/* Ioctl request codes */ -#define BRLVGER_GET_INFO 0 -#define BRLVGER_DISPLAY_ON 2 -#define BRLVGER_DISPLAY_OFF 3 -#define BRLVGER_BUZZ 4 - -/* Base minor for the char devices */ -#define BRLVGER_MINOR 128 - -/* Size of some fields */ -#define BRLVGER_HWVER_SIZE 2 -#define BRLVGER_FWVER_SIZE 200 /* arbitrary, a long string */ -#define BRLVGER_SERIAL_BIN_SIZE 8 -#define BRLVGER_SERIAL_SIZE ((2*BRLVGER_SERIAL_BIN_SIZE)+1) - -struct brlvger_info { - __u8 driver_version[12]; - __u8 driver_banner[200]; - - __u32 display_length; - /* All other char[] fields are strings except this one. - Hardware version: first byte is major, second byte is minor. */ - __u8 hwver[BRLVGER_HWVER_SIZE]; - __u8 fwver[BRLVGER_FWVER_SIZE]; - __u8 serialnum[BRLVGER_SERIAL_SIZE]; -}; - -#endif