Commit 293a4f60 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://kernel.bkbits.net/jgarzik/irda-2.5

into home.osdl.org:/home/torvalds/v2.5/linux
parents 9b32de66 4bec6175
...@@ -1910,7 +1910,7 @@ L: linux-usb-users@lists.sourceforge.net ...@@ -1910,7 +1910,7 @@ L: linux-usb-users@lists.sourceforge.net
L: linux-usb-devel@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net
S: Maintained S: Maintained
USB BLUETOOTH DRIVER USB BLUETOOTH TTY CONVERTER DRIVER
P: Greg Kroah-Hartman P: Greg Kroah-Hartman
M: greg@kroah.com M: greg@kroah.com
L: linux-usb-users@lists.sourceforge.net L: linux-usb-users@lists.sourceforge.net
......
...@@ -146,6 +146,7 @@ struct usblp { ...@@ -146,6 +146,7 @@ struct usblp {
int rcomplete; /* reading is completed */ int rcomplete; /* reading is completed */
unsigned int quirks; /* quirks flags */ unsigned int quirks; /* quirks flags */
unsigned char used; /* True if open */ unsigned char used; /* True if open */
unsigned char present; /* True if not disconnected */
unsigned char bidir; /* interface is bidirectional */ unsigned char bidir; /* interface is bidirectional */
unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */ unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */
/* first 2 bytes are (big-endian) length */ /* first 2 bytes are (big-endian) length */
...@@ -157,6 +158,7 @@ static void usblp_dump(struct usblp *usblp) { ...@@ -157,6 +158,7 @@ static void usblp_dump(struct usblp *usblp) {
dbg("usblp=0x%p", usblp); dbg("usblp=0x%p", usblp);
dbg("dev=0x%p", usblp->dev); dbg("dev=0x%p", usblp->dev);
dbg("present=%d", usblp->present);
dbg("buf=0x%p", usblp->buf); dbg("buf=0x%p", usblp->buf);
dbg("readcount=%d", usblp->readcount); dbg("readcount=%d", usblp->readcount);
dbg("ifnum=%d", usblp->ifnum); dbg("ifnum=%d", usblp->ifnum);
...@@ -253,7 +255,7 @@ static void usblp_bulk_read(struct urb *urb, struct pt_regs *regs) ...@@ -253,7 +255,7 @@ static void usblp_bulk_read(struct urb *urb, struct pt_regs *regs)
{ {
struct usblp *usblp = urb->context; struct usblp *usblp = urb->context;
if (!usblp || !usblp->dev || !usblp->used) if (!usblp || !usblp->dev || !usblp->used || !usblp->present)
return; return;
if (unlikely(urb->status)) if (unlikely(urb->status))
...@@ -267,7 +269,7 @@ static void usblp_bulk_write(struct urb *urb, struct pt_regs *regs) ...@@ -267,7 +269,7 @@ static void usblp_bulk_write(struct urb *urb, struct pt_regs *regs)
{ {
struct usblp *usblp = urb->context; struct usblp *usblp = urb->context;
if (!usblp || !usblp->dev || !usblp->used) if (!usblp || !usblp->dev || !usblp->used || !usblp->present)
return; return;
if (unlikely(urb->status)) if (unlikely(urb->status))
...@@ -332,7 +334,7 @@ static int usblp_open(struct inode *inode, struct file *file) ...@@ -332,7 +334,7 @@ static int usblp_open(struct inode *inode, struct file *file)
goto out; goto out;
} }
usblp = usb_get_intfdata (intf); usblp = usb_get_intfdata (intf);
if (!usblp || !usblp->dev) if (!usblp || !usblp->dev || !usblp->present)
goto out; goto out;
retval = -EBUSY; retval = -EBUSY;
...@@ -404,7 +406,7 @@ static int usblp_release(struct inode *inode, struct file *file) ...@@ -404,7 +406,7 @@ static int usblp_release(struct inode *inode, struct file *file)
down (&usblp->sem); down (&usblp->sem);
lock_kernel(); lock_kernel();
usblp->used = 0; usblp->used = 0;
if (usblp->dev) { if (usblp->present) {
usblp_unlink_urbs(usblp); usblp_unlink_urbs(usblp);
up(&usblp->sem); up(&usblp->sem);
} else /* finish cleanup from disconnect */ } else /* finish cleanup from disconnect */
...@@ -432,7 +434,7 @@ static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -432,7 +434,7 @@ static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
int retval = 0; int retval = 0;
down (&usblp->sem); down (&usblp->sem);
if (!usblp->dev) { if (!usblp->present) {
retval = -ENODEV; retval = -ENODEV;
goto done; goto done;
} }
...@@ -630,7 +632,7 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t ...@@ -630,7 +632,7 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
} }
down (&usblp->sem); down (&usblp->sem);
if (!usblp->dev) { if (!usblp->present) {
up (&usblp->sem); up (&usblp->sem);
return -ENODEV; return -ENODEV;
} }
...@@ -691,7 +693,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count, ...@@ -691,7 +693,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count,
return -EINVAL; return -EINVAL;
down (&usblp->sem); down (&usblp->sem);
if (!usblp->dev) { if (!usblp->present) {
count = -ENODEV; count = -ENODEV;
goto done; goto done;
} }
...@@ -726,7 +728,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count, ...@@ -726,7 +728,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count,
remove_wait_queue(&usblp->wait, &wait); remove_wait_queue(&usblp->wait, &wait);
} }
if (!usblp->dev) { if (!usblp->present) {
count = -ENODEV; count = -ENODEV;
goto done; goto done;
} }
...@@ -916,6 +918,8 @@ static int usblp_probe(struct usb_interface *intf, ...@@ -916,6 +918,8 @@ static int usblp_probe(struct usb_interface *intf,
usb_set_intfdata (intf, usblp); usb_set_intfdata (intf, usblp);
usblp->present = 1;
return 0; return 0;
abort_minor: abort_minor:
...@@ -1115,14 +1119,14 @@ static void usblp_disconnect(struct usb_interface *intf) ...@@ -1115,14 +1119,14 @@ static void usblp_disconnect(struct usb_interface *intf)
down (&usblp->sem); down (&usblp->sem);
lock_kernel(); lock_kernel();
usblp->dev = NULL; usblp->present = 0;
usb_set_intfdata (intf, NULL); usb_set_intfdata (intf, NULL);
usblp_unlink_urbs(usblp); usblp_unlink_urbs(usblp);
if (!usblp->used) if (!usblp->used)
usblp_cleanup (usblp); usblp_cleanup (usblp);
else /* cleanup later, on close */ else /* cleanup later, on release */
up (&usblp->sem); up (&usblp->sem);
unlock_kernel(); unlock_kernel();
} }
......
...@@ -491,10 +491,6 @@ static struct file_system_type usbdevice_fs_type; ...@@ -491,10 +491,6 @@ static struct file_system_type usbdevice_fs_type;
static struct super_block *usb_get_sb(struct file_system_type *fs_type, static struct super_block *usb_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data) int flags, const char *dev_name, void *data)
{ {
if (fs_type == &usbdevice_fs_type)
printk (KERN_INFO "Please use the 'usbfs' filetype instead, "
"the 'usbdevfs' name is deprecated.\n");
return get_sb_single(fs_type, flags, data, usbfs_fill_super); return get_sb_single(fs_type, flags, data, usbfs_fill_super);
} }
......
...@@ -986,7 +986,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) ...@@ -986,7 +986,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
if (err < 0) { if (err < 0) {
err("error getting string descriptor 0 (error=%d)", err); err("error getting string descriptor 0 (error=%d)", err);
goto errout; goto errout;
} else if (tbuf[0] < 4) { } else if (err < 4 || tbuf[0] < 4) {
err("string descriptor 0 too short"); err("string descriptor 0 too short");
err = -EINVAL; err = -EINVAL;
goto errout; goto errout;
......
...@@ -2391,7 +2391,7 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) ...@@ -2391,7 +2391,7 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
if (retval) { if (retval) {
devdbg (dev, "drop, code %d", retval); devdbg (dev, "drop, code %d", retval);
drop: drop:
retval = NET_XMIT_DROP; retval = NET_XMIT_SUCCESS;
dev->stats.tx_dropped++; dev->stats.tx_dropped++;
if (skb) if (skb)
dev_kfree_skb_any (skb); dev_kfree_skb_any (skb);
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
* Copyright (C) 1999 - 2001 * Copyright (C) 1999 - 2001
* Greg Kroah-Hartman (greg@kroah.com) * Greg Kroah-Hartman (greg@kroah.com)
* Bill Ryder (bryder@sgi.com) * Bill Ryder (bryder@sgi.com)
* Copyright (C) 2002
* Kuba Ober (kuba@mareimbrium.org)
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -13,49 +15,156 @@ ...@@ -13,49 +15,156 @@
* See Documentation/usb/usb-serial.txt for more information on using this driver * See Documentation/usb/usb-serial.txt for more information on using this driver
* *
* See http://ftdi-usb-sio.sourceforge.net for upto date testing info * See http://ftdi-usb-sio.sourceforge.net for upto date testing info
* and extra documentation * and extra documentation
*
* (27/Jun/2003) Ian Abbott
* Reworked the urb handling logic. We have no more pool, but dynamically
* allocate the urb and the transfer buffer on the fly. In testing this
* does not incure any measurable overhead. This also relies on the fact
* that we have proper reference counting logic for urbs. I nicked this
* from Greg KH's Visor driver.
*
* (23/Jun/2003) Ian Abbott
* Reduced flip buffer pushes and corrected a data length test in
* ftdi_read_bulk_callback.
* Defererence pointers after any paranoid checks, not before.
*
* (21/Jun/2003) Erik Nygren
* Added support for Home Electronics Tira-1 IR tranceiver using FT232BM chip.
* See <http://www.home-electro.com/tira1.htm>. Only operates properly
* at 100000 and RTS-CTS, so set custom divisor mode on startup.
* Also force the Tira-1 and USB-UIRT to only use their custom baud rates.
*
* (18/Jun/2003) Ian Abbott
* Added Device ID of the USB relais from Rudolf Gugler (backported from
* Philipp Gühring's patch for 2.5.x kernel).
* Moved read transfer buffer reallocation into startup function.
* Free existing write urb and transfer buffer in startup function.
* Only use urbs in write urb pool that were successfully allocated.
* Moved some constant macros out of functions.
* Minor whitespace and comment changes.
*
* (12/Jun/2003) David Norwood
* Added support for USB-UIRT IR tranceiver using 8U232AM chip.
* See <http://home.earthlink.net/~jrhees/USBUIRT/index.htm>. Only
* operates properly at 312500, so set custom divisor mode on startup.
*
* (12/Jun/2003) Ian Abbott
* Added Sealevel SeaLINK+ 210x, 220x, 240x, 280x vid/pids from Tuan Hoang
* - I've eliminated some that don't seem to exist!
* Added Home Electronics Tira-1 IR transceiver pid from Chris Horn
* Some whitespace/coding-style cleanups
*
* (11/Jun/2003) Ian Abbott
* Fixed unsafe spinlock usage in ftdi_write
*
* (24/Feb/2003) Richard Shooter
* Increase read buffer size to improve read speeds at higher baud rates
* (specifically tested with up to 1Mb/sec at 1.5M baud)
*
* (23/Feb/2003) John Wilkins
* Added Xon/xoff flow control (activating support in the ftdi device)
* Added vid/pid for Videonetworks/Homechoice (UK ISP)
*
* (23/Feb/2003) Bill Ryder
* Added matrix orb device vid/pids from Wayne Wylupski
*
* (19/Feb/2003) Ian Abbott
* For TIOCSSERIAL, set alt_speed to 0 when ASYNC_SPD_MASK value has
* changed to something other than ASYNC_SPD_HI, ASYNC_SPD_VHI,
* ASYNC_SPD_SHI or ASYNC_SPD_WARP. Also, unless ASYNC_SPD_CUST is in
* force, don't bother changing baud rate when custom_divisor has changed.
*
* (18/Feb/2003) Ian Abbott
* Fixed TIOCMGET handling to include state of DTR and RTS, the state
* of which are now saved by set_dtr() and set_rts().
* Fixed improper storage class for buf in set_dtr() and set_rts().
* Added FT232BM chip type and support for its extra baud rates (compared
* to FT8U232AM).
* Took account of special case divisor values for highest baud rates of
* FT8U232AM and FT232BM.
* For TIOCSSERIAL, forced alt_speed to 0 when ASYNC_SPD_CUST kludge used,
* as previous alt_speed setting is now stale.
* Moved startup code common between the startup routines for the
* different chip types into a common subroutine.
*
* (17/Feb/2003) Bill Ryder
* Added write urb buffer pool on a per device basis
* Added more checking for open file on callbacks (fixed OOPS)
* Added CrystalFontz 632 and 634 PIDs
* (thanx to CrystalFontz for the sample devices - they flushed out
* some driver bugs)
* Minor debugging message changes
* Added throttle, unthrottle and chars_in_buffer functions
* Fixed FTDI_SIO (the original device) bug
* Fixed some shutdown handling
*
*
* *
*
* (07/Jun/2002) Kuba Ober
* Changed FTDI_SIO_BASE_BAUD_TO_DIVISOR macro into ftdi_baud_to_divisor
* function. It was getting too complex.
* Fix the divisor calculation logic which was setting divisor of 0.125
* instead of 0.5 for fractional parts of divisor equal to 5/8, 6/8, 7/8.
* Also make it bump up the divisor to next integer in case of 7/8 - it's
* a better approximation.
*
* (25/Jul/2002) Bill Ryder inserted Dmitri's TIOCMIWAIT patch
* Not tested by me but it doesn't break anything I use.
*
* (04/Jan/2002) Kuba Ober
* Implemented 38400 baudrate kludge, where it can be substituted with other
* values. That's the only way to set custom baudrates.
* Implemented TIOCSSERIAL, TIOCGSERIAL ioctl's so that setserial is happy.
* FIXME: both baudrate things should eventually go to usbserial.c as other
* devices may need that functionality too. Actually, it can probably be
* merged in serial.c somehow - too many drivers repeat this code over
* and over.
* Fixed baudrate forgetfulness - open() used to reset baudrate to 9600 every time.
* Divisors for baudrates are calculated by a macro.
* Small code cleanups. Ugly whitespace changes for Plato's sake only ;-].
*
* (04/Nov/2001) Bill Ryder * (04/Nov/2001) Bill Ryder
* Fixed bug in read_bulk_callback where incorrect urb buffer was used. * Fixed bug in read_bulk_callback where incorrect urb buffer was used.
* cleaned up write offset calculation * Cleaned up write offset calculation
* added write_room since default values can be incorrect for sio * Added write_room since default values can be incorrect for sio
* changed write_bulk_callback to use same queue_task as other drivers * Changed write_bulk_callback to use same queue_task as other drivers
* (the previous version caused panics) * (the previous version caused panics)
* Removed port iteration code since the device only has one I/O port and it * Removed port iteration code since the device only has one I/O port and it
* was wrong anyway. * was wrong anyway.
* *
* (31/May/2001) gkh * (31/May/2001) gkh
* switched from using spinlock to a semaphore, which fixes lots of problems. * Switched from using spinlock to a semaphore, which fixes lots of problems.
* *
* (23/May/2001) Bill Ryder * (23/May/2001) Bill Ryder
* Added runtime debug patch (thanx Tyson D Sawyer). * Added runtime debug patch (thanx Tyson D Sawyer).
* Cleaned up comments for 8U232 * Cleaned up comments for 8U232
* Added parity, framing and overrun error handling * Added parity, framing and overrun error handling
* Added receive break handling. * Added receive break handling.
* *
* (04/08/2001) gb * (04/08/2001) gb
* Identify version on module load. * Identify version on module load.
* *
* (18/March/2001) Bill Ryder * (18/March/2001) Bill Ryder
* (Not released) * (Not released)
* Added send break handling. (requires kernel patch too) * Added send break handling. (requires kernel patch too)
* Fixed 8U232AM hardware RTS/CTS etc status reporting. * Fixed 8U232AM hardware RTS/CTS etc status reporting.
* Added flipbuf fix copied from generic device * Added flipbuf fix copied from generic device
* *
* (12/3/2000) Bill Ryder * (12/3/2000) Bill Ryder
* Added support for 8U232AM device. * Added support for 8U232AM device.
* Moved PID and VIDs into header file only. * Moved PID and VIDs into header file only.
* Turned on low-latency for the tty (device will do high baudrates) * Turned on low-latency for the tty (device will do high baudrates)
* Added shutdown routine to close files when device removed. * Added shutdown routine to close files when device removed.
* More debug and error message cleanups. * More debug and error message cleanups.
*
* *
* (11/13/2000) Bill Ryder * (11/13/2000) Bill Ryder
* Added spinlock protected open code and close code. * Added spinlock protected open code and close code.
* Multiple opens work (sort of - see webpage mentioned above). * Multiple opens work (sort of - see webpage mentioned above).
* Cleaned up comments. Removed multiple PID/VID definitions. * Cleaned up comments. Removed multiple PID/VID definitions.
* Factorised cts/dtr code * Factorised cts/dtr code
* Made use of __FUNCTION__ in dbg's * Made use of __FUNCTION__ in dbg's
* *
* (11/01/2000) Adam J. Richter * (11/01/2000) Adam J. Richter
* usb_device_id table support * usb_device_id table support
...@@ -72,16 +181,16 @@ ...@@ -72,16 +181,16 @@
* driver is a loadable module now. * driver is a loadable module now.
* *
* (04/04/2000) Bill Ryder * (04/04/2000) Bill Ryder
* Fixed bugs in TCGET/TCSET ioctls (by removing them - they are * Fixed bugs in TCGET/TCSET ioctls (by removing them - they are
* handled elsewhere in the tty io driver chain). * handled elsewhere in the tty io driver chain).
* *
* (03/30/2000) Bill Ryder * (03/30/2000) Bill Ryder
* Implemented lots of ioctls * Implemented lots of ioctls
* Fixed a race condition in write * Fixed a race condition in write
* Changed some dbg's to errs * Changed some dbg's to errs
* *
* (03/26/2000) gkh * (03/26/2000) gkh
* Split driver up into device specific pieces. * Split driver up into device specific pieces.
* *
*/ */
...@@ -90,7 +199,6 @@ ...@@ -90,7 +199,6 @@
/* to talk to the device */ /* to talk to the device */
/* Thanx to gkh and the rest of the usb dev group for all code I have assimilated :-) */ /* Thanx to gkh and the rest of the usb dev group for all code I have assimilated :-) */
#include <linux/config.h> #include <linux/config.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -103,6 +211,7 @@ ...@@ -103,6 +211,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/serial.h>
#ifdef CONFIG_USB_SERIAL_DEBUG #ifdef CONFIG_USB_SERIAL_DEBUG
static int debug = 1; static int debug = 1;
#else #else
...@@ -112,47 +221,244 @@ ...@@ -112,47 +221,244 @@
#include "usb-serial.h" #include "usb-serial.h"
#include "ftdi_sio.h" #include "ftdi_sio.h"
/* /*
* Version Information * Version Information
*/ */
#define DRIVER_VERSION "v1.2.0" #define DRIVER_VERSION "v1.4.0"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>" #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>"
#define DRIVER_DESC "USB FTDI RS232 Converters Driver" #define DRIVER_DESC "USB FTDI Serial Converters Driver"
static struct usb_device_id id_table_sio [] = { static struct usb_device_id id_table_sio [] = {
{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
{ } /* Terminating entry */ { } /* Terminating entry */
}; };
/* THe 8U232AM has the same API as the sio except for: /*
- it can support MUCH higher baudrates (921600 at 48MHz/230400 * The 8U232AM has the same API as the sio except for:
at 12MHz so .. it's baudrate setting codes are different * - it can support MUCH higher baudrates; up to:
- it has a two byte status code. * o 921600 for RS232 and 2000000 for RS422/485 at 48MHz
- it returns characters very 16ms (the FTDI does it every 40ms) * o 230400 at 12MHz
*/ * so .. 8U232AM's baudrate setting codes are different
* - it has a two byte status code.
* - it returns characters every 16ms (the FTDI does it every 40ms)
*
* the bcdDevice value is used to differentiate FT232BM and FT245BM from
* the earlier FT8U232AM and FT8U232BM. For now, include all known VID/PID
* combinations in both tables.
* FIXME: perhaps bcdDevice can also identify 12MHz devices, but I don't know
* if those ever went into mass production. [Ian Abbott]
*/
static struct usb_device_id id_table_8U232AM [] = { static struct usb_device_id id_table_8U232AM [] = {
{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0, 0x3ff) },
{ USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, { USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0, 0x3ff) },
{ USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, { USB_DEVICE_VER(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_634_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_VNHCPCUSB_D_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2101_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2102_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2103_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2104_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2201_1_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2201_2_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2202_1_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2202_2_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2203_1_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2203_2_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_1_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_2_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_3_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_4_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_1_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_2_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_3_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_4_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_1_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_2_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_3_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_4_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_1_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_2_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_3_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_4_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_5_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_6_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_7_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_8_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_1_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_2_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_3_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_4_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_5_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_6_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_7_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_8_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_1_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_2_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_3_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_4_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_5_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_6_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_7_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0, 0x3ff) },
{ } /* Terminating entry */ { } /* Terminating entry */
}; };
static struct usb_device_id id_table_combined [] = { static struct usb_device_id id_table_FT232BM [] = {
{ USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_634_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_VNHCPCUSB_D_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2101_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2102_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2103_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2104_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2201_1_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2201_2_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2202_1_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2202_2_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2203_1_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2203_2_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_1_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_2_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_3_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2401_4_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_1_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_2_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_3_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2402_4_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_1_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_2_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_3_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2403_4_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_1_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_2_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_3_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_4_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_5_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_6_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_7_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2801_8_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_1_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_2_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_3_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_4_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_5_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_6_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_7_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2802_8_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_1_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_2_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_3_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_4_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_5_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_6_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_7_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0x400, 0xffff) },
{ } /* Terminating entry */
};
static struct usb_device_id id_table_USB_UIRT [] = {
{ USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) },
{ } /* Terminating entry */
};
static struct usb_device_id id_table_HE_TIRA1 [] = {
{ USB_DEVICE_VER(FTDI_VID, FTDI_HE_TIRA1_PID, 0x400, 0xffff) },
{ } /* Terminating entry */
};
static __devinitdata struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_XF_634_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) },
{ USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) },
{ USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID) },
{ USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID) },
{ USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID) },
{ USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID) },
{ USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID) },
{ USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID) },
{ USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_3_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_4_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_3_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_4_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_3_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_4_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_3_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_4_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_5_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_6_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_7_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_8_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_3_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_4_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_5_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_6_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_7_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_8_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_3_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_4_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_5_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_6_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_7_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_HE_TIRA1_PID, 0x400, 0xffff) },
{ USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) },
{ } /* Terminating entry */ { } /* Terminating entry */
}; };
MODULE_DEVICE_TABLE (usb, id_table_combined); MODULE_DEVICE_TABLE (usb, id_table_combined);
static struct usb_driver ftdi_driver = { static struct usb_driver ftdi_driver = {
.owner = THIS_MODULE,
.name = "ftdi_sio", .name = "ftdi_sio",
.probe = usb_serial_probe, .probe = usb_serial_probe,
.disconnect = usb_serial_disconnect, .disconnect = usb_serial_disconnect,
...@@ -160,31 +466,63 @@ static struct usb_driver ftdi_driver = { ...@@ -160,31 +466,63 @@ static struct usb_driver ftdi_driver = {
}; };
/* Constants for read urb and write urb */
#define BUFSZ 512
#define PKTSZ 64
struct ftdi_private { struct ftdi_private {
enum ftdi_type ftdi_type; ftdi_chip_type_t chip_type;
__u16 last_set_data_urb_value ; /* the last data state set - needed for doing a break */ /* type of the device, either SIO or FT8U232AM */
int write_offset; int baud_base; /* baud base clock for divisor setting */
int custom_divisor; /* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */
__u16 last_set_data_urb_value ;
/* the last data state set - needed for doing a break */
int write_offset; /* This is the offset in the usb data block to write the serial data -
* it is different between devices
*/
int flags; /* some ASYNC_xxxx flags are supported */
unsigned long last_dtr_rts; /* saved modem control outputs */
wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
char prev_status, diff_status; /* Used for TIOCMIWAIT */
int force_baud; /* if non-zero, force the baud rate to this value */
int force_rtscts; /* if non-zero, force RTS-CTS to always be enabled */
}; };
/* Used for TIOCMIWAIT */
#define FTDI_STATUS_B0_MASK (FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD)
#define FTDI_STATUS_B1_MASK (FTDI_RS_BI)
/* End TIOCMIWAIT */
#define FTDI_IMPL_ASYNC_FLAGS = ( ASYNC_SPD_HI | ASYNC_SPD_VHI \
ASYNC_SPD_CUST | ASYNC_SPD_SHI | ASYNC_SPD_WARP )
/* function prototypes for a FTDI serial converter */ /* function prototypes for a FTDI serial converter */
static int ftdi_sio_startup (struct usb_serial *serial); static int ftdi_SIO_startup (struct usb_serial *serial);
static int ftdi_8U232AM_startup (struct usb_serial *serial); static int ftdi_8U232AM_startup (struct usb_serial *serial);
static void ftdi_sio_shutdown (struct usb_serial *serial); static int ftdi_FT232BM_startup (struct usb_serial *serial);
static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp); static int ftdi_USB_UIRT_startup (struct usb_serial *serial);
static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp); static int ftdi_HE_TIRA1_startup (struct usb_serial *serial);
static int ftdi_sio_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); static void ftdi_shutdown (struct usb_serial *serial);
static int ftdi_sio_write_room (struct usb_serial_port *port); static int ftdi_open (struct usb_serial_port *port, struct file *filp);
static void ftdi_sio_write_bulk_callback (struct urb *urb, struct pt_regs *regs); static void ftdi_close (struct usb_serial_port *port, struct file *filp);
static void ftdi_sio_read_bulk_callback (struct urb *urb, struct pt_regs *regs); static int ftdi_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * old); static int ftdi_write_room (struct usb_serial_port *port);
static int ftdi_sio_tiocmget (struct usb_serial_port *port, struct file *file); static int ftdi_chars_in_buffer (struct usb_serial_port *port);
static int ftdi_sio_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
static void ftdi_sio_break_ctl (struct usb_serial_port *port, int break_state ); static void ftdi_set_termios (struct usb_serial_port *port, struct termios * old);
static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
/* Should rename most ftdi_sio's to ftdi_ now since there are two devices static void ftdi_break_ctl (struct usb_serial_port *port, int break_state );
which share common code */ static void ftdi_throttle (struct usb_serial_port *port);
static void ftdi_unthrottle (struct usb_serial_port *port);
static struct usb_serial_device_type ftdi_sio_device = {
static unsigned short int ftdi_232am_baud_base_to_divisor (int baud, int base);
static unsigned short int ftdi_232am_baud_to_divisor (int baud);
static __u32 ftdi_232bm_baud_base_to_divisor (int baud, int base);
static __u32 ftdi_232bm_baud_to_divisor (int baud);
static struct usb_serial_device_type ftdi_SIO_device = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "FTDI SIO", .name = "FTDI SIO",
.id_table = id_table_sio, .id_table = id_table_sio,
...@@ -192,77 +530,206 @@ static struct usb_serial_device_type ftdi_sio_device = { ...@@ -192,77 +530,206 @@ static struct usb_serial_device_type ftdi_sio_device = {
.num_bulk_in = 1, .num_bulk_in = 1,
.num_bulk_out = 1, .num_bulk_out = 1,
.num_ports = 1, .num_ports = 1,
.open = ftdi_sio_open, .open = ftdi_open,
.close = ftdi_sio_close, .close = ftdi_close,
.write = ftdi_sio_write, .throttle = ftdi_throttle,
.write_room = ftdi_sio_write_room, .unthrottle = ftdi_unthrottle,
.read_bulk_callback = ftdi_sio_read_bulk_callback, .write = ftdi_write,
.write_bulk_callback = ftdi_sio_write_bulk_callback, .write_room = ftdi_write_room,
.ioctl = ftdi_sio_ioctl, .chars_in_buffer = ftdi_chars_in_buffer,
.set_termios = ftdi_sio_set_termios, .read_bulk_callback = ftdi_read_bulk_callback,
.break_ctl = ftdi_sio_break_ctl, .write_bulk_callback = ftdi_write_bulk_callback,
.tiocmget = ftdi_sio_tiocmget, .ioctl = ftdi_ioctl,
.tiocmset = ftdi_sio_tiocmset, .set_termios = ftdi_set_termios,
.attach = ftdi_sio_startup, .break_ctl = ftdi_break_ctl,
.shutdown = ftdi_sio_shutdown, .attach = ftdi_SIO_startup,
.shutdown = ftdi_shutdown,
}; };
static struct usb_serial_device_type ftdi_8U232AM_device = { static struct usb_serial_device_type ftdi_8U232AM_device = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "FTDI 8U232AM", .name = "FTDI 8U232AM Compatible",
.id_table = id_table_8U232AM, .id_table = id_table_8U232AM,
.num_interrupt_in = 0, .num_interrupt_in = 0,
.num_bulk_in = 1, .num_bulk_in = 1,
.num_bulk_out = 1, .num_bulk_out = 1,
.num_ports = 1, .num_ports = 1,
.open = ftdi_sio_open, .open = ftdi_open,
.close = ftdi_sio_close, .close = ftdi_close,
.write = ftdi_sio_write, .throttle = ftdi_throttle,
.write_room = ftdi_sio_write_room, .unthrottle = ftdi_unthrottle,
.read_bulk_callback = ftdi_sio_read_bulk_callback, .write = ftdi_write,
.write_bulk_callback = ftdi_sio_write_bulk_callback, .write_room = ftdi_write_room,
.ioctl = ftdi_sio_ioctl, .chars_in_buffer = ftdi_chars_in_buffer,
.set_termios = ftdi_sio_set_termios, .read_bulk_callback = ftdi_read_bulk_callback,
.break_ctl = ftdi_sio_break_ctl, .write_bulk_callback = ftdi_write_bulk_callback,
.tiocmget = ftdi_sio_tiocmget, .ioctl = ftdi_ioctl,
.tiocmset = ftdi_sio_tiocmset, .set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
.attach = ftdi_8U232AM_startup, .attach = ftdi_8U232AM_startup,
.shutdown = ftdi_sio_shutdown, .shutdown = ftdi_shutdown,
}; };
static struct usb_serial_device_type ftdi_FT232BM_device = {
.owner = THIS_MODULE,
.name = "FTDI FT232BM Compatible",
.id_table = id_table_FT232BM,
.num_interrupt_in = 0,
.num_bulk_in = 1,
.num_bulk_out = 1,
.num_ports = 1,
.open = ftdi_open,
.close = ftdi_close,
.throttle = ftdi_throttle,
.unthrottle = ftdi_unthrottle,
.write = ftdi_write,
.write_room = ftdi_write_room,
.chars_in_buffer = ftdi_chars_in_buffer,
.read_bulk_callback = ftdi_read_bulk_callback,
.write_bulk_callback = ftdi_write_bulk_callback,
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
.attach = ftdi_FT232BM_startup,
.shutdown = ftdi_shutdown,
};
static struct usb_serial_device_type ftdi_USB_UIRT_device = {
.owner = THIS_MODULE,
.name = "USB-UIRT Infrared Receiver/Transmitter",
.id_table = id_table_USB_UIRT,
.num_interrupt_in = 0,
.num_bulk_in = 1,
.num_bulk_out = 1,
.num_ports = 1,
.open = ftdi_open,
.close = ftdi_close,
.throttle = ftdi_throttle,
.unthrottle = ftdi_unthrottle,
.write = ftdi_write,
.write_room = ftdi_write_room,
.chars_in_buffer = ftdi_chars_in_buffer,
.read_bulk_callback = ftdi_read_bulk_callback,
.write_bulk_callback = ftdi_write_bulk_callback,
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
.attach = ftdi_USB_UIRT_startup,
.shutdown = ftdi_shutdown,
};
/* The TIRA1 is based on a FT232BM which requires a fixed baud rate of 100000
* and which requires RTS-CTS to be enabled. */
static struct usb_serial_device_type ftdi_HE_TIRA1_device = {
.owner = THIS_MODULE,
.name = "Home-Electronics TIRA-1 IR Transceiver",
.id_table = id_table_HE_TIRA1,
.num_interrupt_in = 0,
.num_bulk_in = 1,
.num_bulk_out = 1,
.num_ports = 1,
.open = ftdi_open,
.close = ftdi_close,
.throttle = ftdi_throttle,
.unthrottle = ftdi_unthrottle,
.write = ftdi_write,
.write_room = ftdi_write_room,
.chars_in_buffer = ftdi_chars_in_buffer,
.read_bulk_callback = ftdi_read_bulk_callback,
.write_bulk_callback = ftdi_write_bulk_callback,
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
.attach = ftdi_HE_TIRA1_startup,
.shutdown = ftdi_shutdown,
};
#define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */
/* High and low are for DTR, RTS etc etc */
#define HIGH 1
#define LOW 0
/* /*
* *************************************************************************** * ***************************************************************************
* FTDI SIO Serial Converter specific driver functions * Utlity functions
* *************************************************************************** * ***************************************************************************
*/ */
#define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base)
{
unsigned short int divisor;
int divisor3 = base / 2 / baud; // divisor shifted 3 bits to the left
if ((divisor3 & 0x7) == 7) divisor3 ++; // round x.7/8 up to x+1
divisor = divisor3 >> 3;
divisor3 &= 0x7;
if (divisor3 == 1) divisor |= 0xc000; else // 0.125
if (divisor3 >= 4) divisor |= 0x4000; else // 0.5
if (divisor3 != 0) divisor |= 0x8000; // 0.25
if (divisor == 1) divisor = 0; /* special case for maximum baud rate */
return divisor;
}
/* utility functions to set and unset dtr and rts */ static unsigned short int ftdi_232am_baud_to_divisor(int baud)
#define HIGH 1
#define LOW 0
static int set_rts(struct usb_device *dev,
unsigned int pipe,
int high_or_low)
{ {
static char buf[1]; return(ftdi_232am_baud_base_to_divisor(baud, 48000000));
unsigned ftdi_high_or_low = (high_or_low? FTDI_SIO_SET_RTS_HIGH : }
FTDI_SIO_SET_RTS_LOW);
return(usb_control_msg(dev, pipe, static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base)
{
static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
__u32 divisor;
int divisor3 = base / 2 / baud; // divisor shifted 3 bits to the left
divisor = divisor3 >> 3;
divisor |= (__u32)divfrac[divisor3 & 0x7] << 14;
/* Deal with special cases for highest baud rates. */
if (divisor == 1) divisor = 0; else // 1.0
if (divisor == 0x4001) divisor = 1; // 1.5
return divisor;
}
static __u32 ftdi_232bm_baud_to_divisor(int baud)
{
return(ftdi_232bm_baud_base_to_divisor(baud, 48000000));
}
static int set_rts(struct usb_serial_port *port, int high_or_low)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
char buf[1];
unsigned ftdi_high_or_low;
if (high_or_low) {
ftdi_high_or_low = FTDI_SIO_SET_RTS_HIGH;
priv->last_dtr_rts |= TIOCM_RTS;
} else {
ftdi_high_or_low = FTDI_SIO_SET_RTS_LOW;
priv->last_dtr_rts &= ~TIOCM_RTS;
}
return(usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST,
FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
ftdi_high_or_low, 0, ftdi_high_or_low, 0,
buf, 0, WDR_TIMEOUT)); buf, 0, WDR_TIMEOUT));
} }
static int set_dtr(struct usb_device *dev,
unsigned int pipe,
int high_or_low) static int set_dtr(struct usb_serial_port *port, int high_or_low)
{ {
static char buf[1]; struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned ftdi_high_or_low = (high_or_low? FTDI_SIO_SET_DTR_HIGH : char buf[1];
FTDI_SIO_SET_DTR_LOW); unsigned ftdi_high_or_low;
return(usb_control_msg(dev, pipe, if (high_or_low) {
ftdi_high_or_low = FTDI_SIO_SET_DTR_HIGH;
priv->last_dtr_rts |= TIOCM_DTR;
} else {
ftdi_high_or_low = FTDI_SIO_SET_DTR_LOW;
priv->last_dtr_rts &= ~TIOCM_DTR;
}
return(usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST,
FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
ftdi_high_or_low, 0, ftdi_high_or_low, 0,
...@@ -270,71 +737,409 @@ static int set_dtr(struct usb_device *dev, ...@@ -270,71 +737,409 @@ static int set_dtr(struct usb_device *dev,
} }
static __u32 get_ftdi_divisor(struct usb_serial_port * port);
static int ftdi_sio_startup (struct usb_serial *serial)
static int change_speed(struct usb_serial_port *port)
{ {
struct ftdi_private *priv; char buf[1];
__u16 urb_value;
__u16 urb_index;
__u32 urb_index_value;
urb_index_value = get_ftdi_divisor(port);
urb_value = (__u16)urb_index_value;
urb_index = (__u16)(urb_index_value >> 16);
priv = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL); return (usb_control_msg(port->serial->dev,
if (!priv){ usb_sndctrlpipe(port->serial->dev, 0),
err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct ftdi_private)); FTDI_SIO_SET_BAUDRATE_REQUEST,
return -ENOMEM; FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
urb_value, urb_index,
buf, 0, 100) < 0);
}
static __u32 get_ftdi_divisor(struct usb_serial_port * port)
{ /* get_ftdi_divisor */
struct ftdi_private *priv = usb_get_serial_port_data(port);
__u32 div_value = 0;
int div_okay = 1;
char *chip_name = "";
int baud;
/*
* The logic involved in setting the baudrate can be cleanly split in 3 steps.
* Obtaining the actual baud rate is a little tricky since unix traditionally
* somehow ignored the possibility to set non-standard baud rates.
* 1. Standard baud rates are set in tty->termios->c_cflag
* 2. If these are not enough, you can set any speed using alt_speed as follows:
* - set tty->termios->c_cflag speed to B38400
* - set your real speed in tty->alt_speed; it gets ignored when
* alt_speed==0, (or)
* - call TIOCSSERIAL ioctl with (struct serial_struct) set as follows:
* flags & ASYNC_SPD_MASK == ASYNC_SPD_[HI, VHI, SHI, WARP], this just
* sets alt_speed to (HI: 57600, VHI: 115200, SHI: 230400, WARP: 460800)
* ** Steps 1, 2 are done courtesy of tty_get_baud_rate
* 3. You can also set baud rate by setting custom divisor as follows
* - set tty->termios->c_cflag speed to B38400
* - call TIOCSSERIAL ioctl with (struct serial_struct) set as follows:
* o flags & ASYNC_SPD_MASK == ASYNC_SPD_CUST
* o custom_divisor set to baud_base / your_new_baudrate
* ** Step 3 is done courtesy of code borrowed from serial.c - I should really
* spend some time and separate+move this common code to serial.c, it is
* replicated in nearly every serial driver you see.
*/
/* 1. Get the baud rate from the tty settings, this observes alt_speed hack */
baud = tty_get_baud_rate(port->tty);
dbg("%s - tty_get_baud_rate reports speed %d", __FUNCTION__, baud);
/* 2. Observe async-compatible custom_divisor hack, update baudrate if needed */
if (baud == 38400 &&
((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) &&
(priv->custom_divisor)) {
baud = priv->baud_base / priv->custom_divisor;
dbg("%s - custom divisor %d sets baud rate to %d", __FUNCTION__, priv->custom_divisor, baud);
} }
priv->ftdi_type = sio; /* 3. Convert baudrate to device-specific divisor */
priv->write_offset = 1;
usb_set_serial_port_data(serial->port, priv); if (!baud) baud = 9600;
switch(priv->chip_type) {
case SIO: /* SIO chip */
chip_name = "SIO";
switch(baud) {
case 300: div_value = ftdi_sio_b300; break;
case 600: div_value = ftdi_sio_b600; break;
case 1200: div_value = ftdi_sio_b1200; break;
case 2400: div_value = ftdi_sio_b2400; break;
case 4800: div_value = ftdi_sio_b4800; break;
case 9600: div_value = ftdi_sio_b9600; break;
case 19200: div_value = ftdi_sio_b19200; break;
case 38400: div_value = ftdi_sio_b38400; break;
case 57600: div_value = ftdi_sio_b57600; break;
case 115200: div_value = ftdi_sio_b115200; break;
} /* baud */
if (div_value == 0) {
dbg("%s - Baudrate (%d) requested is not supported", __FUNCTION__, baud);
div_value = ftdi_sio_b9600;
div_okay = 0;
}
break;
case FT8U232AM: /* 8U232AM chip */
chip_name = "FT8U232AM";
if (baud <= 3000000) {
div_value = ftdi_232am_baud_to_divisor(baud);
} else {
dbg("%s - Baud rate too high!", __FUNCTION__);
div_value = ftdi_232am_baud_to_divisor(9600);
div_okay = 0;
}
break;
case FT232BM: /* FT232BM chip */
chip_name = "FT232BM";
if (baud <= 3000000) {
div_value = ftdi_232bm_baud_to_divisor(baud);
} else {
dbg("%s - Baud rate too high!", __FUNCTION__);
div_value = ftdi_232bm_baud_to_divisor(9600);
div_okay = 0;
}
break;
} /* priv->chip_type */
return (0); if (div_okay) {
dbg("%s - Baud rate set to %d (divisor 0x%lX) on chip %s",
__FUNCTION__, baud, (unsigned long)div_value, chip_name);
}
return(div_value);
} }
static int ftdi_8U232AM_startup (struct usb_serial *serial) static int get_serial_info(struct usb_serial_port * port, struct serial_struct * retinfo)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct serial_struct tmp;
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.flags = priv->flags;
tmp.baud_base = priv->baud_base;
tmp.custom_divisor = priv->custom_divisor;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
} /* get_serial_info */
static int set_serial_info(struct usb_serial_port * port, struct serial_struct * newinfo)
{ /* set_serial_info */
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct serial_struct new_serial;
struct ftdi_private old_priv;
if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
return -EFAULT;
old_priv = * priv;
/* Do error checking and permission checking */
if (!capable(CAP_SYS_ADMIN)) {
if (((new_serial.flags & ~ASYNC_USR_MASK) !=
(priv->flags & ~ASYNC_USR_MASK)))
return -EPERM;
priv->flags = ((priv->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
priv->custom_divisor = new_serial.custom_divisor;
goto check_and_exit;
}
if ((new_serial.baud_base != priv->baud_base) ||
(new_serial.baud_base < 9600))
return -EINVAL;
/* Make the changes - these are privileged changes! */
priv->flags = ((priv->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
priv->custom_divisor = new_serial.custom_divisor;
port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
check_and_exit:
if ((old_priv.flags & ASYNC_SPD_MASK) !=
(priv->flags & ASYNC_SPD_MASK)) {
if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
port->tty->alt_speed = 57600;
else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
port->tty->alt_speed = 115200;
else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
port->tty->alt_speed = 230400;
else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
port->tty->alt_speed = 460800;
else
port->tty->alt_speed = 0;
}
if (((old_priv.flags & ASYNC_SPD_MASK) !=
(priv->flags & ASYNC_SPD_MASK)) ||
(((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) &&
(old_priv.custom_divisor != priv->custom_divisor))) {
change_speed(port);
}
return (0);
} /* set_serial_info */
/*
* ***************************************************************************
* FTDI driver specific functions
* ***************************************************************************
*/
/* Common startup subroutine */
/* Called from ftdi_SIO_startup, etc. */
static int ftdi_common_startup (struct usb_serial *serial)
{ {
struct usb_serial_port *port = &serial->port[0];
struct ftdi_private *priv; struct ftdi_private *priv;
dbg("%s",__FUNCTION__);
priv = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL); priv = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL);
if (!priv){ if (!priv){
err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct ftdi_private)); err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct ftdi_private));
return -ENOMEM; return -ENOMEM;
} }
memset(priv, 0, sizeof(*priv));
priv->ftdi_type = F8U232AM; init_waitqueue_head(&priv->delta_msr_wait);
priv->write_offset = 0; /* This will push the characters through immediately rather
usb_set_serial_port_data(serial->port, priv); than queue a task to deliver them */
priv->flags = ASYNC_LOW_LATENCY;
/* Increase the size of read buffers */
if (port->bulk_in_buffer) {
kfree (port->bulk_in_buffer);
}
port->bulk_in_buffer = kmalloc (BUFSZ, GFP_KERNEL);
if (!port->bulk_in_buffer) {
kfree (priv);
return -ENOMEM;
}
if (port->read_urb) {
port->read_urb->transfer_buffer = port->bulk_in_buffer;
port->read_urb->transfer_buffer_length = BUFSZ;
}
/* Free port's existing write urb and transfer buffer. */
if (port->write_urb) {
usb_free_urb (port->write_urb);
port->write_urb = NULL;
}
if (port->bulk_out_buffer) {
kfree (port->bulk_out_buffer);
port->bulk_out_buffer = NULL;
}
usb_set_serial_port_data(serial->port, priv);
return (0); return (0);
} }
static void ftdi_sio_shutdown (struct usb_serial *serial)
/* Startup for the SIO chip */
/* Called from usbserial:serial_probe */
static int ftdi_SIO_startup (struct usb_serial *serial)
{ {
void *priv; struct ftdi_private *priv;
int err;
dbg("%s", __FUNCTION__); dbg("%s",__FUNCTION__);
priv = usb_get_serial_port_data(&serial->port[0]); err = ftdi_common_startup(serial);
if (priv){ if (err){
kfree(priv); return (err);
usb_set_serial_port_data(&serial->port[0], NULL);
} }
priv = usb_get_serial_port_data(serial->port);
priv->chip_type = SIO;
priv->baud_base = 12000000 / 16;
priv->write_offset = 1;
return (0);
} }
/* Startup for the 8U232AM chip */
/* Called from usbserial:serial_probe */
static int ftdi_8U232AM_startup (struct usb_serial *serial)
{ /* ftdi_8U232AM_startup */
struct ftdi_private *priv;
int err;
dbg("%s",__FUNCTION__);
err = ftdi_common_startup(serial);
if (err){
return (err);
}
priv = usb_get_serial_port_data(serial->port);
priv->chip_type = FT8U232AM;
priv->baud_base = 48000000 / 2; /* Would be / 16, but FTDI supports 0.125, 0.25 and 0.5 divisor fractions! */
return (0);
} /* ftdi_8U232AM_startup */
/* Startup for the FT232BM chip */
/* Called from usbserial:serial_probe */
static int ftdi_FT232BM_startup (struct usb_serial *serial)
{ /* ftdi_FT232BM_startup */
struct ftdi_private *priv;
int err;
dbg("%s",__FUNCTION__);
err = ftdi_common_startup(serial);
if (err){
return (err);
}
priv = usb_get_serial_port_data(serial->port);
priv->chip_type = FT232BM;
priv->baud_base = 48000000 / 2; /* Would be / 16, but FT232BM supports multiple of 0.125 divisor fractions! */
return (0);
} /* ftdi_FT232BM_startup */
/* Startup for the USB-UIRT device, which requires hardwired baudrate (38400 gets mapped to 312500) */
/* Called from usbserial:serial_probe */
static int ftdi_USB_UIRT_startup (struct usb_serial *serial)
{ /* ftdi_USB_UIRT_startup */
struct ftdi_private *priv;
int err;
dbg("%s",__FUNCTION__);
err = ftdi_8U232AM_startup(serial);
if (err){
return (err);
}
priv = usb_get_serial_port_data(serial->port);
priv->flags |= ASYNC_SPD_CUST;
priv->custom_divisor = 77;
priv->force_baud = B38400;
return (0);
} /* ftdi_USB_UIRT_startup */
/* Startup for the HE-TIRA1 device, which requires hardwired
* baudrate (38400 gets mapped to 100000) */
static int ftdi_HE_TIRA1_startup (struct usb_serial *serial)
{ /* ftdi_HE_TIRA1_startup */
struct ftdi_private *priv;
int err;
dbg("%s",__FUNCTION__);
err = ftdi_FT232BM_startup(serial);
if (err){
return (err);
}
priv = usb_get_serial_port_data(serial->port);
priv->flags |= ASYNC_SPD_CUST;
priv->custom_divisor = 240;
priv->force_baud = B38400;
priv->force_rtscts = 1;
return (0);
} /* ftdi_HE_TIRA1_startup */
/* ftdi_shutdown is called from usbserial:usb_serial_disconnect
* it is called when the usb device is disconnected
*
* usbserial:usb_serial_disconnect
* calls __serial_close for each open of the port
* shutdown is called then (ie ftdi_shutdown)
*/
static void ftdi_shutdown (struct usb_serial *serial)
{ /* ftdi_shutdown */
struct usb_serial_port *port = serial->port;
struct ftdi_private *priv = usb_get_serial_port_data(port);
dbg("%s", __FUNCTION__);
/* all open ports are closed at this point
* (by usbserial.c:__serial_close, which calls ftdi_close)
*/
if (priv) {
kfree(priv);
usb_set_serial_port_data(port, NULL);
}
} /* ftdi_shutdown */
static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp) static int ftdi_open (struct usb_serial_port *port, struct file *filp)
{ /* ftdi_sio_open */ { /* ftdi_open */
struct termios tmp_termios; struct termios tmp_termios;
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
struct ftdi_private *priv = usb_get_serial_port_data(port);
int result = 0; int result = 0;
char buf[1]; /* Needed for the usb_control_msg I think */ char buf[1]; /* Needed for the usb_control_msg I think */
dbg("%s", __FUNCTION__); dbg("%s", __FUNCTION__);
/* This will push the characters through immediately rather
than queue a task to deliver them */ port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
port->tty->low_latency = 1;
/* No error checking for this (will get errors later anyway) */ /* No error checking for this (will get errors later anyway) */
/* See ftdi_sio.h for description of what is reset */ /* See ftdi_sio.h for description of what is reset */
...@@ -343,19 +1148,20 @@ static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp) ...@@ -343,19 +1148,20 @@ static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp)
FTDI_SIO_RESET_SIO, FTDI_SIO_RESET_SIO,
0, buf, 0, WDR_TIMEOUT); 0, buf, 0, WDR_TIMEOUT);
/* Setup termios defaults. According to tty_io.c the /* Termios defaults are set by usb_serial_init. We don't change
settings are driver specific */ port->tty->termios - this would loose speed settings, etc.
port->tty->termios->c_cflag = This is same behaviour as serial.c/rs_open() - Kuba */
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
/* ftdi_sio_set_termios will send usb control messages */ /* ftdi_set_termios will send usb control messages */
ftdi_sio_set_termios(port, &tmp_termios); ftdi_set_termios(port, &tmp_termios);
/* FIXME: Flow control might be enabled, so it should be checked -
we have no control of defaults! */
/* Turn on RTS and DTR since we are not flow controlling by default */ /* Turn on RTS and DTR since we are not flow controlling by default */
if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0) { if (set_dtr(port, HIGH) < 0) {
err("%s Error from DTR HIGH urb", __FUNCTION__); err("%s Error from DTR HIGH urb", __FUNCTION__);
} }
if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0){ if (set_rts(port, HIGH) < 0){
err("%s Error from RTS HIGH urb", __FUNCTION__); err("%s Error from RTS HIGH urb", __FUNCTION__);
} }
...@@ -363,23 +1169,37 @@ static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp) ...@@ -363,23 +1169,37 @@ static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp)
usb_fill_bulk_urb(port->read_urb, serial->dev, usb_fill_bulk_urb(port->read_urb, serial->dev,
usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
ftdi_sio_read_bulk_callback, port); ftdi_read_bulk_callback, port);
result = usb_submit_urb(port->read_urb, GFP_KERNEL); result = usb_submit_urb(port->read_urb, GFP_KERNEL);
if (result) if (result)
err("%s - failed submitting read urb, error %d", __FUNCTION__, result); err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
return result; return result;
} /* ftdi_sio_open */ } /* ftdi_open */
static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp) /*
{ /* ftdi_sio_close */ * usbserial:__serial_close only calls ftdi_close if the point is open
struct usb_serial *serial = port->serial; /* Checked in usbserial.c */ *
* This only gets called when it is the last close
*
*
*/
static void ftdi_close (struct usb_serial_port *port, struct file *filp)
{ /* ftdi_close */
struct usb_serial *serial;
unsigned int c_cflag = port->tty->termios->c_cflag; unsigned int c_cflag = port->tty->termios->c_cflag;
char buf[1]; char buf[1];
dbg("%s", __FUNCTION__); dbg("%s", __FUNCTION__);
serial = get_usb_serial ( port, __FUNCTION__);
if (!serial)
return;
if (serial->dev) { if (serial->dev) {
if (c_cflag & HUPCL){ if (c_cflag & HUPCL){
/* Disable flow control */ /* Disable flow control */
...@@ -392,38 +1212,46 @@ static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp) ...@@ -392,38 +1212,46 @@ static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp)
} }
/* drop DTR */ /* drop DTR */
if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) < 0){ if (set_dtr(port, LOW) < 0){
err("Error from DTR LOW urb"); err("Error from DTR LOW urb");
} }
/* drop RTS */ /* drop RTS */
if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0) { if (set_rts(port, LOW) < 0) {
err("Error from RTS LOW urb"); err("Error from RTS LOW urb");
} }
/* shutdown our bulk read */
if (port->read_urb) {
usb_unlink_urb (port->read_urb);
}
/* unlink the running write urbs */
} /* Note change no line is hupcl is off */ } /* Note change no line is hupcl is off */
} /* if (serial->dev) */
/* shutdown our bulk reads and writes */
/* ***CHECK*** behaviour when there is nothing queued */ } /* ftdi_close */
usb_unlink_urb (port->write_urb);
usb_unlink_urb (port->read_urb);
}
} /* ftdi_sio_close */
/* The ftdi_sio requires the first byte to have: /* The SIO requires the first byte to have:
* B0 1 * B0 1
* B1 0 * B1 0
* B2..7 length of message excluding byte 0 * B2..7 length of message excluding byte 0
*
* The new devices do not require this byte
*/ */
static int ftdi_sio_write (struct usb_serial_port *port, int from_user, static int ftdi_write (struct usb_serial_port *port, int from_user,
const unsigned char *buf, int count) const unsigned char *buf, int count)
{ /* ftdi_sio_write */ { /* ftdi_write */
struct usb_serial *serial = port->serial; struct usb_serial *serial = get_usb_serial ( port, __FUNCTION__);
struct ftdi_private *priv = usb_get_serial_port_data(port); struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned char *first_byte = port->write_urb->transfer_buffer; struct urb *urb;
int data_offset ; unsigned char *buffer;
int result; int data_offset ; /* will be 1 for the SIO and 0 otherwise */
int status;
int transfer_size;
dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count); dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count);
if (count == 0) { if (count == 0) {
...@@ -434,123 +1262,209 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user, ...@@ -434,123 +1262,209 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user,
data_offset = priv->write_offset; data_offset = priv->write_offset;
dbg("data_offset set to %d",data_offset); dbg("data_offset set to %d",data_offset);
if (port->write_urb->status == -EINPROGRESS) { /* Determine total transfer size */
dbg("%s - already writing", __FUNCTION__); transfer_size = count;
return (0); if (data_offset > 0) {
} /* Original sio needs control bytes too... */
transfer_size += (data_offset *
((count + (PKTSZ - 1 - data_offset)) /
(PKTSZ - data_offset)));
}
count += data_offset; buffer = kmalloc (transfer_size, GFP_ATOMIC);
count = (count > port->bulk_out_size) ? port->bulk_out_size : count; if (!buffer) {
err("%s ran out of kernel memory for urb ...", __FUNCTION__);
return -ENOMEM;
}
/* Copy in the data to send */ urb = usb_alloc_urb(0, GFP_ATOMIC);
if (from_user) { if (!urb) {
if (copy_from_user(port->write_urb->transfer_buffer + data_offset, err("%s - no more free urbs", __FUNCTION__);
buf, count - data_offset )){ kfree (buffer);
return -EFAULT; return -ENOMEM;
}
/* Copy data */
if (data_offset > 0) {
/* Original sio requires control byte at start of each packet. */
int user_pktsz = PKTSZ - data_offset;
int todo = count;
unsigned char *first_byte = buffer;
const unsigned char *current_position = buf;
while (todo > 0) {
if (user_pktsz > todo) {
user_pktsz = todo;
}
/* Write the control byte at the front of the packet*/
*first_byte = 1 | ((user_pktsz) << 2);
/* Copy data for packet */
if (from_user) {
if (copy_from_user (first_byte + data_offset,
current_position, user_pktsz)){
kfree (buffer);
usb_free_urb (urb);
return -EFAULT;
}
} else {
memcpy (first_byte + data_offset,
current_position, user_pktsz);
}
first_byte += user_pktsz + data_offset;
current_position += user_pktsz;
todo -= user_pktsz;
} }
} else { } else {
memcpy(port->write_urb->transfer_buffer + data_offset, /* No control byte required. */
buf, count - data_offset ); /* Copy in the data to send */
} if (from_user) {
if (copy_from_user (buffer, buf, count)) {
first_byte = port->write_urb->transfer_buffer; kfree (buffer);
if (data_offset > 0){ usb_free_urb (urb);
/* Write the control byte at the front of the packet*/ return -EFAULT;
*first_byte = 1 | ((count-data_offset) << 2) ; }
} else {
memcpy (buffer, buf, count);
}
} }
dbg("%s Bytes: %d, First Byte: 0x%02x", __FUNCTION__,count, first_byte[0]); usb_serial_debug_data (__FILE__, __FUNCTION__, transfer_size, buffer);
usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte);
/* fill the buffer and send it */
/* send the data out the bulk port */ usb_fill_bulk_urb(urb, serial->dev,
usb_fill_bulk_urb(port->write_urb, serial->dev,
usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, count, buffer, transfer_size,
ftdi_sio_write_bulk_callback, port); ftdi_write_bulk_callback, port);
result = usb_submit_urb(port->write_urb, GFP_ATOMIC); status = usb_submit_urb(urb, GFP_ATOMIC);
if (result) { if (status) {
err("%s - failed submitting write urb, error %d", __FUNCTION__, result); err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
return 0; count = status;
} }
dbg("%s write returning: %d", __FUNCTION__, count - data_offset); /* we are done with this urb, so let the host driver
return (count - data_offset); * really free it when it is finished with it */
} /* ftdi_sio_write */ usb_free_urb (urb);
dbg("%s write returning: %d", __FUNCTION__, count);
return count;
} /* ftdi_write */
static void ftdi_sio_write_bulk_callback (struct urb *urb, struct pt_regs *regs) /* This function may get called when the device is closed */
static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
{ {
struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
dbg("%s", __FUNCTION__); dbg("%s", __FUNCTION__);
if (port_paranoia_check (port, "ftdi_sio_write_bulk_callback")) { if (port_paranoia_check (port, __FUNCTION__))
return;
}
serial = port->serial;
if (serial_paranoia_check (serial, "ftdi_sio_write_bulk_callback")) {
return; return;
}
if (urb->status) { if (urb->status) {
dbg("nonzero write bulk status received: %d", urb->status); dbg("nonzero write bulk status received: %d", urb->status);
return; return;
} }
schedule_work(&port->work); if (!serial) {
} /* ftdi_sio_write_bulk_callback */ dbg("%s - bad serial pointer, exiting", __FUNCTION__);
return;
}
/* Have to check for validity of queueing up the tasks */
dbg("%s - port->open_count = %d", __FUNCTION__, port->open_count);
static int ftdi_sio_write_room( struct usb_serial_port *port ) if (port->open_count > 0){
schedule_work(&port->work);
}
return;
} /* ftdi_write_bulk_callback */
static int ftdi_write_room( struct usb_serial_port *port )
{ {
struct ftdi_private *priv = usb_get_serial_port_data(port); dbg("%s - port %d", __FUNCTION__, port->number);
int room;
if ( port->write_urb->status == -EINPROGRESS) { /*
/* There is a race here with the _write routines but it won't hurt */ * We really can take anything the user throws at us
room = 0; * but let's pick a nice big number to tell the tty
} else { * layer that we have lots of free space
room = port->bulk_out_size - priv->write_offset; */
} return 2048;
return(room); } /* ftdi_write_room */
} /* ftdi_sio_write_room */
static int ftdi_chars_in_buffer (struct usb_serial_port *port)
{ /* ftdi_chars_in_buffer */
dbg("%s - port %d", __FUNCTION__, port->number);
/*
* We can't really account for how much data we
* have sent out, but hasn't made it through to the
* device, so just tell the tty layer that everything
* is flushed.
*/
return 0;
} /* ftdi_chars_in_buffer */
static void ftdi_sio_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
{ /* ftdi_sio_serial_buld_callback */ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
{ /* ftdi_read_bulk_callback */
struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial; struct usb_serial *serial;
struct tty_struct *tty = port->tty ; struct tty_struct *tty;
struct ftdi_private *priv;
char error_flag; char error_flag;
unsigned char *data = urb->transfer_buffer; unsigned char *data = urb->transfer_buffer;
const int data_offset = 2;
int i; int i;
int result; int result;
int need_flip;
int packet_offset;
dbg("%s - port %d", __FUNCTION__, port->number); if (urb->number_of_packets > 0) {
err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__,
urb->transfer_buffer_length, urb->actual_length, urb->number_of_packets );
err("%s transfer_flags %x ", __FUNCTION__,urb->transfer_flags );
}
dbg("%s", __FUNCTION__);
if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) { if (port_paranoia_check (port, __FUNCTION__)) {
return; return;
} }
if (port->open_count <= 0)
return;
serial = port->serial; serial = get_usb_serial(port,__FUNCTION__);
if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) { if (!serial){
dbg("%s - bad serial pointer - exiting",__FUNCTION__);
return;
}
tty = port->tty;
if (!tty) {
dbg("%s - bad tty pointer - exiting",__FUNCTION__);
return; return;
} }
priv = usb_get_serial_port_data(port);
if (urb->status) { if (urb->status) {
/* This will happen at close every time so it is a dbg not an err */ /* This will happen at close every time so it is a dbg not an err */
dbg("nonzero read bulk status received: %d", urb->status); dbg("(this is ok on close) nonzero read bulk status received: %d", urb->status);
return; return;
} }
/* The first two bytes of every read packet are status */
if (urb->actual_length > 2) { if (urb->actual_length > 2) {
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
} else { } else {
dbg("Just status 0o%03o0o%03o",data[0],data[1]); dbg("Status only: %03oo %03oo",data[0],data[1]);
} }
...@@ -559,129 +1473,101 @@ static void ftdi_sio_read_bulk_callback (struct urb *urb, struct pt_regs *regs) ...@@ -559,129 +1473,101 @@ static void ftdi_sio_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
/* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */ /* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */
/* if CD is dropped and the line is not CLOCAL then we should hangup */ /* if CD is dropped and the line is not CLOCAL then we should hangup */
/* Handle errors and break */ need_flip = 0;
error_flag = TTY_NORMAL; for (packet_offset=0; packet_offset < urb->actual_length; packet_offset += PKTSZ) {
/* Although the device uses a bitmask and hence can have multiple */ /* Compare new line status to the old one, signal if different */
/* errors on a packet - the order here sets the priority the */ if (priv != NULL) {
/* error is returned to the tty layer */ char new_status = data[packet_offset+0] & FTDI_STATUS_B0_MASK;
if (new_status != priv->prev_status) {
if ( data[1] & FTDI_RS_OE ) { priv->diff_status |= new_status ^ priv->prev_status;
error_flag = TTY_OVERRUN; wake_up_interruptible(&priv->delta_msr_wait);
dbg("OVERRRUN error"); priv->prev_status = new_status;
}
if ( data[1] & FTDI_RS_BI ) {
error_flag = TTY_BREAK;
dbg("BREAK received");
}
if ( data[1] & FTDI_RS_PE ) {
error_flag = TTY_PARITY;
dbg("PARITY error");
}
if ( data[1] & FTDI_RS_FE ) {
error_flag = TTY_FRAME;
dbg("FRAMING error");
}
if (urb->actual_length > data_offset) {
for (i = data_offset ; i < urb->actual_length ; ++i) {
/* have to make sure we don't overflow the buffer
with tty_insert_flip_char's */
if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty_flip_buffer_push(tty);
} }
/* Note that the error flag is duplicated for
every character received since we don't know
which character it applied to */
tty_insert_flip_char(tty, data[i], error_flag);
} }
tty_flip_buffer_push(tty);
/* Handle errors and break */
error_flag = TTY_NORMAL;
/* Although the device uses a bitmask and hence can have multiple */
/* errors on a packet - the order here sets the priority the */
/* error is returned to the tty layer */
} if ( data[packet_offset+1] & FTDI_RS_OE ) {
error_flag = TTY_OVERRUN;
dbg("OVERRRUN error");
}
if ( data[packet_offset+1] & FTDI_RS_BI ) {
error_flag = TTY_BREAK;
dbg("BREAK received");
}
if ( data[packet_offset+1] & FTDI_RS_PE ) {
error_flag = TTY_PARITY;
dbg("PARITY error");
}
if ( data[packet_offset+1] & FTDI_RS_FE ) {
error_flag = TTY_FRAME;
dbg("FRAMING error");
}
if (urb->actual_length > packet_offset + 2) {
for (i = 2; (i < PKTSZ) && ((i+packet_offset) < urb->actual_length); ++i) {
/* have to make sure we don't overflow the buffer
with tty_insert_flip_char's */
if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty_flip_buffer_push(tty);
}
/* Note that the error flag is duplicated for
every character received since we don't know
which character it applied to */
tty_insert_flip_char(tty, data[packet_offset+i], error_flag);
}
need_flip = 1;
}
#ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW #ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW
/* if a parity error is detected you get status packets forever /* if a parity error is detected you get status packets forever
until a character is sent without a parity error. until a character is sent without a parity error.
This doesn't work well since the application receives a never This doesn't work well since the application receives a never
ending stream of bad data - even though new data hasn't been sent. ending stream of bad data - even though new data hasn't been sent.
Therefore I (bill) have taken this out. Therefore I (bill) have taken this out.
However - this might make sense for framing errors and so on However - this might make sense for framing errors and so on
so I am leaving the code in for now. so I am leaving the code in for now.
*/ */
else { else {
if (error_flag != TTY_NORMAL){ if (error_flag != TTY_NORMAL){
dbg("error_flag is not normal"); dbg("error_flag is not normal");
/* In this case it is just status - if that is an error send a bad character */ /* In this case it is just status - if that is an error send a bad character */
if(tty->flip.count >= TTY_FLIPBUF_SIZE) { if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty_flip_buffer_push(tty); tty_flip_buffer_push(tty);
} }
tty_insert_flip_char(tty, 0xff, error_flag); tty_insert_flip_char(tty, 0xff, error_flag);
tty_flip_buffer_push(tty); need_flip = 1;
}
} }
}
#endif #endif
} /* "for(packet_offset=0..." */
/* Continue trying to always read */ /* Low latency */
usb_fill_bulk_urb(port->read_urb, serial->dev, if (need_flip) {
usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), tty_flip_buffer_push(tty);
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, }
ftdi_sio_read_bulk_callback, port);
result = usb_submit_urb(port->read_urb, GFP_ATOMIC); /* if the port is closed stop trying to read */
if (result) if (port->open_count > 0){
err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); /* Continue trying to always read */
usb_fill_bulk_urb(port->read_urb, serial->dev,
usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
ftdi_read_bulk_callback, port);
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
}
return; return;
} /* ftdi_sio_serial_read_bulk_callback */ } /* ftdi_read_bulk_callback */
static __u16 translate_baudrate_to_ftdi(unsigned int cflag, enum ftdi_type ftdi_type) static void ftdi_break_ctl( struct usb_serial_port *port, int break_state )
{ /* translate_baudrate_to_ftdi */
__u16 urb_value = ftdi_sio_b9600;
if (ftdi_type == sio){
switch(cflag & CBAUD){
case B0: break; /* ignored by this */
case B300: urb_value = ftdi_sio_b300; dbg("Set to 300"); break;
case B600: urb_value = ftdi_sio_b600; dbg("Set to 600") ; break;
case B1200: urb_value = ftdi_sio_b1200; dbg("Set to 1200") ; break;
case B2400: urb_value = ftdi_sio_b2400; dbg("Set to 2400") ; break;
case B4800: urb_value = ftdi_sio_b4800; dbg("Set to 4800") ; break;
case B9600: urb_value = ftdi_sio_b9600; dbg("Set to 9600") ; break;
case B19200: urb_value = ftdi_sio_b19200; dbg("Set to 19200") ; break;
case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break;
case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break;
case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break;
default: dbg("%s - FTDI_SIO does not support the baudrate (%d) requested",
__FUNCTION__, (cflag & CBAUD));
break;
}
} else { /* it is 8U232AM */
switch(cflag & CBAUD){
case B0: break; /* ignored by this */
case B300: urb_value = ftdi_8U232AM_48MHz_b300; dbg("Set to 300"); break;
case B600: urb_value = ftdi_8U232AM_48MHz_b600; dbg("Set to 600") ; break;
case B1200: urb_value = ftdi_8U232AM_48MHz_b1200; dbg("Set to 1200") ; break;
case B2400: urb_value = ftdi_8U232AM_48MHz_b2400; dbg("Set to 2400") ; break;
case B4800: urb_value = ftdi_8U232AM_48MHz_b4800; dbg("Set to 4800") ; break;
case B9600: urb_value = ftdi_8U232AM_48MHz_b9600; dbg("Set to 9600") ; break;
case B19200: urb_value = ftdi_8U232AM_48MHz_b19200; dbg("Set to 19200") ; break;
case B38400: urb_value = ftdi_8U232AM_48MHz_b38400; dbg("Set to 38400") ; break;
case B57600: urb_value = ftdi_8U232AM_48MHz_b57600; dbg("Set to 57600") ; break;
case B115200: urb_value = ftdi_8U232AM_48MHz_b115200; dbg("Set to 115200") ; break;
case B230400: urb_value = ftdi_8U232AM_48MHz_b230400; dbg("Set to 230400") ; break;
case B460800: urb_value = ftdi_8U232AM_48MHz_b460800; dbg("Set to 460800") ; break;
case B921600: urb_value = ftdi_8U232AM_48MHz_b921600; dbg("Set to 921600") ; break;
default: dbg("%s - The baudrate (%d) requested is not implemented",
__FUNCTION__, (cflag & CBAUD));
break;
}
}
return(urb_value);
}
static void ftdi_sio_break_ctl( struct usb_serial_port *port, int break_state )
{ {
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
struct ftdi_private *priv = usb_get_serial_port_data(port); struct ftdi_private *priv = usb_get_serial_port_data(port);
...@@ -704,32 +1590,48 @@ static void ftdi_sio_break_ctl( struct usb_serial_port *port, int break_state ) ...@@ -704,32 +1590,48 @@ static void ftdi_sio_break_ctl( struct usb_serial_port *port, int break_state )
FTDI_SIO_SET_DATA_REQUEST_TYPE, FTDI_SIO_SET_DATA_REQUEST_TYPE,
urb_value , 0, urb_value , 0,
buf, 0, WDR_TIMEOUT) < 0) { buf, 0, WDR_TIMEOUT) < 0) {
err("%s - FAILED to enable/disable break state (state was %d)", err("%s FAILED to enable/disable break state (state was %d)", __FUNCTION__,break_state);
__FUNCTION__, break_state);
} }
dbg("%s - break state is %d - urb is %d", __FUNCTION__, break_state, urb_value); dbg("%s break state is %d - urb is %d", __FUNCTION__,break_state, urb_value);
} }
/* old_termios contains the original termios settings and tty->termios contains
* the new setting to be used
* WARNING: set_termios calls this with old_termios in kernel space
*/
/* As I understand this - old_termios contains the original termios settings */ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_termios)
/* and tty->termios contains the new setting to be used */ { /* ftdi_termios */
/* */
/* WARNING: set_termios calls this with old_termios in kernel space */
static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{ /* ftdi_sio_set_termios */
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
unsigned int cflag = port->tty->termios->c_cflag; unsigned int cflag = port->tty->termios->c_cflag;
struct ftdi_private *priv = usb_get_serial_port_data(port); struct ftdi_private *priv = usb_get_serial_port_data(port);
__u16 urb_value; /* will hold the new flags */ __u16 urb_value; /* will hold the new flags */
char buf[1]; /* Perhaps I should dynamically alloc this? */ char buf[1]; /* Perhaps I should dynamically alloc this? */
// Added for xon/xoff support
unsigned int iflag = port->tty->termios->c_iflag;
unsigned char vstop;
unsigned char vstart;
dbg("%s", __FUNCTION__); dbg("%s", __FUNCTION__);
/* Force baud rate if this device requires it, unless it is set to B0. */
if (priv->force_baud && ((port->tty->termios->c_cflag & CBAUD) != B0)) {
dbg("%s: forcing baud rate for this device", __FUNCTION__);
port->tty->termios->c_cflag &= ~CBAUD;
port->tty->termios->c_cflag |= priv->force_baud;
}
/* Force RTS-CTS if this device requires it. */
if (priv->force_rtscts) {
dbg("%s: forcing rtscts for this device", __FUNCTION__);
port->tty->termios->c_cflag |= CRTSCTS;
}
cflag = port->tty->termios->c_cflag;
/* FIXME -For this cut I don't care if the line is really changing or /* FIXME -For this cut I don't care if the line is really changing or
not - so just do the change regardless - should be able to not - so just do the change regardless - should be able to
...@@ -771,8 +1673,6 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * ...@@ -771,8 +1673,6 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *
} }
/* Now do the baudrate */ /* Now do the baudrate */
urb_value = translate_baudrate_to_ftdi((cflag & CBAUD), priv->ftdi_type);
if ((cflag & CBAUD) == B0 ) { if ((cflag & CBAUD) == B0 ) {
/* Disable flow control */ /* Disable flow control */
if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
...@@ -783,24 +1683,20 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * ...@@ -783,24 +1683,20 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *
err("%s error from disable flowcontrol urb", __FUNCTION__); err("%s error from disable flowcontrol urb", __FUNCTION__);
} }
/* Drop RTS and DTR */ /* Drop RTS and DTR */
if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){ if (set_dtr(port, LOW) < 0){
err("%s Error from DTR LOW urb", __FUNCTION__); err("%s Error from DTR LOW urb", __FUNCTION__);
} }
if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){ if (set_rts(port, LOW) < 0){
err("%s Error from RTS LOW urb", __FUNCTION__); err("%s Error from RTS LOW urb", __FUNCTION__);
} }
} else { } else {
/* set the baudrate determined before */ /* set the baudrate determined before */
if (usb_control_msg(serial->dev, if (change_speed(port)) {
usb_sndctrlpipe(serial->dev, 0),
FTDI_SIO_SET_BAUDRATE_REQUEST,
FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
urb_value, 0,
buf, 0, 100) < 0) {
err("%s urb failed to set baurdrate", __FUNCTION__); err("%s urb failed to set baurdrate", __FUNCTION__);
} }
} }
/* Set flow control */ /* Set flow control */
/* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */ /* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
if (cflag & CRTSCTS) { if (cflag & CRTSCTS) {
...@@ -815,146 +1711,290 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * ...@@ -815,146 +1711,290 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *
} }
} else { } else {
/* CHECKME Assuming XON/XOFF handled by tty stack - not by device */ /*
dbg("%s Turning off hardware flow control", __FUNCTION__); * Xon/Xoff code
if (usb_control_msg(serial->dev, *
usb_sndctrlpipe(serial->dev, 0), * Check the IXOFF status in the iflag component of the termios structure
FTDI_SIO_SET_FLOW_CTRL_REQUEST, * if IXOFF is not set, the pre-xon/xoff code is executed.
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, */
0, 0, if (iflag & IXOFF) {
buf, 0, WDR_TIMEOUT) < 0) { dbg("%s request to enable xonxoff iflag=%04x",__FUNCTION__,iflag);
err("urb failed to clear flow control"); // Try to enable the XON/XOFF on the ftdi_sio
} // Set the vstart and vstop -- could have been done up above where
// a lot of other dereferencing is done but that would be very
// inefficient as vstart and vstop are not always needed
vstart=port->tty->termios->c_cc[VSTART];
vstop=port->tty->termios->c_cc[VSTOP];
urb_value=(vstop << 8) | (vstart);
if (usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
urb_value , FTDI_SIO_XON_XOFF_HS,
buf, 0, WDR_TIMEOUT) < 0) {
err("urb failed to set to xon/xoff flow control");
}
} else {
/* else clause to only run if cfag ! CRTSCTS and iflag ! XOFF */
/* CHECKME Assuming XON/XOFF handled by tty stack - not by device */
dbg("%s Turning off hardware flow control", __FUNCTION__);
if (usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
0, 0,
buf, 0, WDR_TIMEOUT) < 0) {
err("urb failed to clear flow control");
}
}
} }
return; return;
} /* ftdi_sio_set_termios */ } /* ftdi_termios */
static int ftdi_sio_tiocmget (struct usb_serial_port *port, struct file *file)
static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
{ {
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
struct ftdi_private *priv = usb_get_serial_port_data(port); struct ftdi_private *priv = usb_get_serial_port_data(port);
char *buf = NULL;
int ret = -EINVAL; __u16 urb_value=0; /* Will hold the new flags */
int size; char buf[2];
int ret, mask;
dbg("%s", __FUNCTION__); dbg("%s cmd 0x%04x", __FUNCTION__, cmd);
buf = kmalloc(2, GFP_KERNEL); /* Based on code from acm.c and others */
if (!buf) switch (cmd) {
goto exit;
if (priv->ftdi_type == sio) { case TIOCMGET:
size = 1; dbg("%s TIOCMGET", __FUNCTION__);
} else { switch (priv->chip_type) {
/* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same case SIO:
format as the data returned from the in point */ /* Request the status from the device */
size = 2; if ((ret = usb_control_msg(serial->dev,
} usb_rcvctrlpipe(serial->dev, 0),
ret = usb_control_msg(serial->dev, FTDI_SIO_GET_MODEM_STATUS_REQUEST,
usb_rcvctrlpipe(serial->dev, 0), FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
FTDI_SIO_GET_MODEM_STATUS_REQUEST, 0, 0,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, buf, 1, WDR_TIMEOUT)) < 0 ) {
0, 0, buf, size, WDR_TIMEOUT); err("%s Could not get modem status of device - err: %d", __FUNCTION__,
if (ret < 0) { ret);
err("%s Could not get modem status of device - err: %d", return(ret);
__FUNCTION__, ret); }
goto exit; break;
} case FT8U232AM:
case FT232BM:
ret = (buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) | /* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same
(buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) | format as the data returned from the in point */
(buf[0] & FTDI_SIO_RI_MASK ? TIOCM_RI : 0) | if ((ret = usb_control_msg(serial->dev,
(buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD : 0); usb_rcvctrlpipe(serial->dev, 0),
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
exit: FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
kfree(buf); 0, 0,
return ret; buf, 2, WDR_TIMEOUT)) < 0 ) {
} err("%s Could not get modem status of device - err: %d", __FUNCTION__,
ret);
return(ret);
}
break;
default:
return -EFAULT;
break;
}
static int ftdi_sio_tiocmset (struct usb_serial_port *port, struct file *file, return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
unsigned int set, unsigned int clear) (buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
{ (buf[0] & FTDI_SIO_RI_MASK ? TIOCM_RI : 0) |
struct usb_serial *serial = port->serial; (buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD : 0) |
int ret = 0; priv->last_dtr_rts,
(unsigned long *) arg);
dbg("%s", __FUNCTION__); break;
if (set & TIOCM_RTS) case TIOCMSET: /* Turns on and off the lines as specified by the mask */
if ((ret = set_rts(serial->dev, dbg("%s TIOCMSET", __FUNCTION__);
usb_sndctrlpipe(serial->dev, 0), if (get_user(mask, (unsigned long *) arg))
HIGH)) < 0) { return -EFAULT;
err("Urb to set RTS failed"); urb_value = ((mask & TIOCM_DTR) ? HIGH : LOW);
goto exit; if ((ret = set_dtr(port, urb_value)) < 0){
err("Error from DTR set urb (TIOCMSET)");
return(ret);
} }
urb_value = ((mask & TIOCM_RTS) ? HIGH : LOW);
if (set & TIOCM_DTR) if ((ret = set_rts(port, urb_value)) < 0){
if ((ret = set_dtr(serial->dev, err("Error from RTS set urb (TIOCMSET)");
usb_sndctrlpipe(serial->dev, 0), return(ret);
HIGH)) < 0) {
err("Urb to set DTR failed");
goto exit;
} }
return(0);
if (clear & TIOCM_RTS) break;
if ((ret = set_rts(serial->dev,
usb_sndctrlpipe(serial->dev, 0), case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */
LOW)) < 0) { dbg("%s TIOCMBIS", __FUNCTION__);
err("Urb to unset RTS failed"); if (get_user(mask, (unsigned long *) arg))
goto exit; return -EFAULT;
if (mask & TIOCM_DTR){
if ((ret = set_dtr(port, HIGH)) < 0) {
err("Urb to set DTR failed");
return(ret);
}
}
if (mask & TIOCM_RTS) {
if ((ret = set_rts(port, HIGH)) < 0){
err("Urb to set RTS failed");
return(ret);
}
} }
return(0);
break;
if (clear & TIOCM_DTR) case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */
if ((ret = set_dtr(serial->dev, dbg("%s TIOCMBIC", __FUNCTION__);
usb_sndctrlpipe(serial->dev, 0), if (get_user(mask, (unsigned long *) arg))
LOW)) < 0) { return -EFAULT;
err("Urb to unset DTR failed"); if (mask & TIOCM_DTR){
goto exit; if ((ret = set_dtr(port, LOW)) < 0){
err("Urb to unset DTR failed");
return(ret);
}
}
if (mask & TIOCM_RTS) {
if ((ret = set_rts(port, LOW)) < 0){
err("Urb to unset RTS failed");
return(ret);
}
} }
return(0);
break;
exit: /*
return ret; * I had originally implemented TCSET{A,S}{,F,W} and
} * TCGET{A,S} here separately, however when testing I
* found that the higher layers actually do the termios
* conversions themselves and pass the call onto
* ftdi_sio_set_termios.
*
*/
case TIOCGSERIAL: /* gets serial port data */
return get_serial_info(port, (struct serial_struct *) arg);
case TIOCSSERIAL: /* sets serial port data */
return set_serial_info(port, (struct serial_struct *) arg);
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was.
*
* This code is borrowed from linux/drivers/char/serial.c
*/
case TIOCMIWAIT:
while (priv != NULL) {
interruptible_sleep_on(&priv->delta_msr_wait);
/* see if a signal did it */
if (signal_pending(current))
return -ERESTARTSYS;
else {
char diff = priv->diff_status;
if (diff == 0) {
return -EIO; /* no change => error */
}
static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) /* Consume all events */
{ priv->diff_status = 0;
dbg("%s cmd 0x%04x", __FUNCTION__, cmd);
switch (cmd) { /* Return 0 if caller wanted to know about these bits */
if ( ((arg & TIOCM_RNG) && (diff & FTDI_RS0_RI)) ||
((arg & TIOCM_DSR) && (diff & FTDI_RS0_DSR)) ||
((arg & TIOCM_CD) && (diff & FTDI_RS0_RLSD)) ||
((arg & TIOCM_CTS) && (diff & FTDI_RS0_CTS)) ) {
return 0;
}
/*
* Otherwise caller can't care less about what happened,
* and so we continue to wait for more events.
*/
}
}
return(0);
break;
default: default:
/* This is not an error - turns out the higher layers will do
* some ioctls itself
*/
dbg("%s arg not supported - it was 0x%04x", __FUNCTION__,cmd);
return(-ENOIOCTLCMD);
break; break;
} }
return 0;
} /* ftdi_sio_ioctl */
static int __init ftdi_sio_init (void) /* This is not necessarily an error - turns out the higher layers will do
* some ioctls itself (see comment above)
*/
dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", __FUNCTION__, cmd);
return(-ENOIOCTLCMD);
} /* ftdi_ioctl */
static void ftdi_throttle (struct usb_serial_port *port)
{
dbg("%s - port %d", __FUNCTION__, port->number);
usb_unlink_urb (port->read_urb);
}
static void ftdi_unthrottle (struct usb_serial_port *port)
{ {
int result;
struct usb_serial *serial = port->serial;
dbg("%s - port %d", __FUNCTION__, port->number);
port->read_urb->dev = serial->dev;
usb_fill_bulk_urb(port->read_urb, serial->dev,
usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
ftdi_read_bulk_callback, port);
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
}
static int __init ftdi_init (void)
{
dbg("%s", __FUNCTION__); dbg("%s", __FUNCTION__);
usb_serial_register (&ftdi_sio_device); usb_serial_register (&ftdi_SIO_device);
usb_serial_register (&ftdi_8U232AM_device); usb_serial_register (&ftdi_8U232AM_device);
usb_serial_register (&ftdi_FT232BM_device);
usb_serial_register (&ftdi_USB_UIRT_device);
usb_serial_register (&ftdi_HE_TIRA1_device);
usb_register (&ftdi_driver); usb_register (&ftdi_driver);
info(DRIVER_VERSION ":" DRIVER_DESC); info(DRIVER_VERSION ":" DRIVER_DESC);
return 0; return 0;
} }
static void __exit ftdi_sio_exit (void) static void __exit ftdi_exit (void)
{ {
dbg("%s", __FUNCTION__); dbg("%s", __FUNCTION__);
usb_deregister (&ftdi_driver); usb_deregister (&ftdi_driver);
usb_serial_deregister (&ftdi_sio_device); usb_serial_deregister (&ftdi_HE_TIRA1_device);
usb_serial_deregister (&ftdi_USB_UIRT_device);
usb_serial_deregister (&ftdi_FT232BM_device);
usb_serial_deregister (&ftdi_8U232AM_device); usb_serial_deregister (&ftdi_8U232AM_device);
usb_serial_deregister (&ftdi_SIO_device);
} }
module_init(ftdi_sio_init); module_init(ftdi_init);
module_exit(ftdi_sio_exit); module_exit(ftdi_exit);
MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_DESCRIPTION( DRIVER_DESC );
......
...@@ -14,11 +14,12 @@ ...@@ -14,11 +14,12 @@
* of the protocol required to talk to the device and ongoing assistence * of the protocol required to talk to the device and ongoing assistence
* during development. * during development.
* *
* Bill Ryder - bryder@sgi.com of Silicon Graphics, Inc.- wrote the * Bill Ryder - bryder@sgi.com formerly of Silicon Graphics, Inc.- wrote the
* FTDI_SIO implementation. * FTDI_SIO implementation.
* *
* Philipp Ghring - pg@futureware.at - added the Device ID of the USB relais * Philipp Ghring - pg@futureware.at - added the Device ID of the USB relais
* from Rudolf Gugler * from Rudolf Gugler
*
*/ */
#define FTDI_VID 0x0403 /* Vendor Id */ #define FTDI_VID 0x0403 /* Vendor Id */
...@@ -28,6 +29,93 @@ ...@@ -28,6 +29,93 @@
#define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ #define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */
#define FTDI_NF_RIC_PID 0x0001 /* Product Id */ #define FTDI_NF_RIC_PID 0x0001 /* Product Id */
/* www.crystalfontz.com devices - thanx for providing free devices for evaluation ! */
/* they use the ftdi chipset for the USB interface and the vendor id is the same */
#define FTDI_XF_634_PID 0xFC09 /* Four line device */
#define FTDI_XF_632_PID 0xFC08 /* Two line device */
/* Video Networks Limited / Homechoice in the UK use an ftdi-based device for their 1Mb */
/* broadband internet service. The following PID is exhibited by the usb device supplied */
/* (the VID is the standard ftdi vid (FTDI_VID) */
#define FTDI_VNHCPCUSB_D_PID 0xfe38 /* Product Id */
/*
* The following are the values for the Matrix Orbital LCD displays,
* which are the FT232BM ( similar to the 8U232AM )
*/
#define FTDI_MTXORB_VID FTDI_VID /* Matrix Orbital Product Id */
#define FTDI_MTXORB_0_PID 0xFA00 /* Matrix Orbital Product Id */
#define FTDI_MTXORB_1_PID 0xFA01 /* Matrix Orbital Product Id */
#define FTDI_MTXORB_2_PID 0xFA02 /* Matrix Orbital Product Id */
#define FTDI_MTXORB_3_PID 0xFA03 /* Matrix Orbital Product Id */
#define FTDI_MTXORB_4_PID 0xFA04 /* Matrix Orbital Product Id */
#define FTDI_MTXORB_5_PID 0xFA05 /* Matrix Orbital Product Id */
#define FTDI_MTXORB_6_PID 0xFA06 /* Matrix Orbital Product Id */
/*
* The following are the values for the Sealevel SeaLINK+ adapters.
* (Original list sent by Tuan Hoang. Ian Abbott renamed the macros and
* removed some PIDs that don't seem to match any existing products.)
*/
#define SEALEVEL_VID 0x0c52 /* Sealevel Vendor ID */
#define SEALEVEL_2101_PID 0x2101 /* SeaLINK+232 (2101/2105) */
#define SEALEVEL_2102_PID 0x2102 /* SeaLINK+485 (2102) */
#define SEALEVEL_2103_PID 0x2103 /* SeaLINK+232I (2103) */
#define SEALEVEL_2104_PID 0x2104 /* SeaLINK+485I (2104) */
#define SEALEVEL_2201_1_PID 0x2211 /* SeaPORT+2/232 (2201) Port 1 */
#define SEALEVEL_2201_2_PID 0x2221 /* SeaPORT+2/232 (2201) Port 2 */
#define SEALEVEL_2202_1_PID 0x2212 /* SeaPORT+2/485 (2202) Port 1 */
#define SEALEVEL_2202_2_PID 0x2222 /* SeaPORT+2/485 (2202) Port 2 */
#define SEALEVEL_2203_1_PID 0x2213 /* SeaPORT+2 (2203) Port 1 */
#define SEALEVEL_2203_2_PID 0x2223 /* SeaPORT+2 (2203) Port 2 */
#define SEALEVEL_2401_1_PID 0x2411 /* SeaPORT+4/232 (2401) Port 1 */
#define SEALEVEL_2401_2_PID 0x2421 /* SeaPORT+4/232 (2401) Port 2 */
#define SEALEVEL_2401_3_PID 0x2431 /* SeaPORT+4/232 (2401) Port 3 */
#define SEALEVEL_2401_4_PID 0x2441 /* SeaPORT+4/232 (2401) Port 4 */
#define SEALEVEL_2402_1_PID 0x2412 /* SeaPORT+4/485 (2402) Port 1 */
#define SEALEVEL_2402_2_PID 0x2422 /* SeaPORT+4/485 (2402) Port 2 */
#define SEALEVEL_2402_3_PID 0x2432 /* SeaPORT+4/485 (2402) Port 3 */
#define SEALEVEL_2402_4_PID 0x2442 /* SeaPORT+4/485 (2402) Port 4 */
#define SEALEVEL_2403_1_PID 0x2413 /* SeaPORT+4 (2403) Port 1 */
#define SEALEVEL_2403_2_PID 0x2423 /* SeaPORT+4 (2403) Port 2 */
#define SEALEVEL_2403_3_PID 0x2433 /* SeaPORT+4 (2403) Port 3 */
#define SEALEVEL_2403_4_PID 0x2443 /* SeaPORT+4 (2403) Port 4 */
#define SEALEVEL_2801_1_PID 0X2811 /* SeaLINK+8/232 (2801) Port 1 */
#define SEALEVEL_2801_2_PID 0X2821 /* SeaLINK+8/232 (2801) Port 2 */
#define SEALEVEL_2801_3_PID 0X2831 /* SeaLINK+8/232 (2801) Port 3 */
#define SEALEVEL_2801_4_PID 0X2841 /* SeaLINK+8/232 (2801) Port 4 */
#define SEALEVEL_2801_5_PID 0X2851 /* SeaLINK+8/232 (2801) Port 5 */
#define SEALEVEL_2801_6_PID 0X2861 /* SeaLINK+8/232 (2801) Port 6 */
#define SEALEVEL_2801_7_PID 0X2871 /* SeaLINK+8/232 (2801) Port 7 */
#define SEALEVEL_2801_8_PID 0X2881 /* SeaLINK+8/232 (2801) Port 8 */
#define SEALEVEL_2802_1_PID 0X2812 /* SeaLINK+8/485 (2802) Port 1 */
#define SEALEVEL_2802_2_PID 0X2822 /* SeaLINK+8/485 (2802) Port 2 */
#define SEALEVEL_2802_3_PID 0X2832 /* SeaLINK+8/485 (2802) Port 3 */
#define SEALEVEL_2802_4_PID 0X2842 /* SeaLINK+8/485 (2802) Port 4 */
#define SEALEVEL_2802_5_PID 0X2852 /* SeaLINK+8/485 (2802) Port 5 */
#define SEALEVEL_2802_6_PID 0X2862 /* SeaLINK+8/485 (2802) Port 6 */
#define SEALEVEL_2802_7_PID 0X2872 /* SeaLINK+8/485 (2802) Port 7 */
#define SEALEVEL_2802_8_PID 0X2882 /* SeaLINK+8/485 (2802) Port 8 */
#define SEALEVEL_2803_1_PID 0X2813 /* SeaLINK+8 (2803) Port 1 */
#define SEALEVEL_2803_2_PID 0X2823 /* SeaLINK+8 (2803) Port 2 */
#define SEALEVEL_2803_3_PID 0X2833 /* SeaLINK+8 (2803) Port 3 */
#define SEALEVEL_2803_4_PID 0X2843 /* SeaLINK+8 (2803) Port 4 */
#define SEALEVEL_2803_5_PID 0X2853 /* SeaLINK+8 (2803) Port 5 */
#define SEALEVEL_2803_6_PID 0X2863 /* SeaLINK+8 (2803) Port 6 */
#define SEALEVEL_2803_7_PID 0X2873 /* SeaLINK+8 (2803) Port 7 */
#define SEALEVEL_2803_8_PID 0X2883 /* SeaLINK+8 (2803) Port 8 */
/*
* Home Electronics (www.home-electro.com) USB gadgets
*/
#define FTDI_HE_TIRA1_PID 0xFA78 /* Tira-1 IR tranceiver */
/* USB-UIRT - An infrared receiver and transmitter using the 8U232AM chip */
/* http://home.earthlink.net/~jrhees/USBUIRT/index.htm */
#define FTDI_USB_UIRT_PID 0xF850 /* Product Id */
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ #define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */
...@@ -84,19 +172,61 @@ ...@@ -84,19 +172,61 @@
/* /*
* BmRequestType: 0100 0000B * BmRequestType: 0100 0000B
* bRequest: FTDI_SIO_SET_BAUDRATE * bRequest: FTDI_SIO_SET_BAUDRATE
* wValue: BaudRate value - see below * wValue: BaudDivisor value - see below
* wIndex: Port * wIndex: Port
* wLength: 0 * wLength: 0
* Data: None * Data: None
* The BaudDivisor values are calculated as follows:
* - BaseClock is either 12000000 or 48000000 depending on the device. FIXME: I wish
* I knew how to detect old chips to select proper base clock!
* - BaudDivisor is a fixed point number encoded in a funny way.
* (--WRONG WAY OF THINKING--)
* BaudDivisor is a fixed point number encoded with following bit weighs:
* (-2)(-1)(13..0). It is a radical with a denominator of 4, so values
* end with 0.0 (00...), 0.25 (10...), 0.5 (01...), and 0.75 (11...).
* (--THE REALITY--)
* The both-bits-set has quite different meaning from 0.75 - the chip designers
* have decided it to mean 0.125 instead of 0.75.
* This info looked up in FTDI application note "FT8U232 DEVICES \ Data Rates
* and Flow Control Consideration for USB to RS232".
* - BaudDivisor = (BaseClock / 16) / BaudRate, where the (=) operation should
* automagically re-encode the resulting value to take fractions into consideration.
* As all values are integers, some bit twiddling is in order:
* BaudDivisor = (BaseClock / 16 / BaudRate) |
* (((BaseClock / 2 / BaudRate) & 4) ? 0x4000 // 0.5
* : ((BaseClock / 2 / BaudRate) & 2) ? 0x8000 // 0.25
* : ((BaseClock / 2 / BaudRate) & 1) ? 0xc000 // 0.125
* : 0)
*
* For the FT232BM, a 17th divisor bit was introduced to encode the multiples
* of 0.125 missing from the FT8U232AM. Bits 16 to 14 are coded as follows
* (the first four codes are the same as for the FT8U232AM, where bit 16 is
* always 0):
* 000 - add .000 to divisor
* 001 - add .500 to divisor
* 010 - add .250 to divisor
* 011 - add .125 to divisor
* 100 - add .375 to divisor
* 101 - add .625 to divisor
* 110 - add .750 to divisor
* 111 - add .875 to divisor
* Bits 15 to 0 of the 17-bit divisor are placed in the urb value. Bit 16 is
* placed in bit 0 of the urb index.
*
* Note that there are a couple of special cases to support the highest baud
* rates. If the calculated divisor value is 1, this needs to be replaced with
* 0. Additionally for the FT232BM, if the calculated divisor value is 0x4001
* (1.5), this needs to be replaced with 0x0001 (1) (but this divisor value is
* not supported by the FT8U232AM).
*/ */
enum ftdi_type { typedef enum {
sio = 1, SIO = 1,
F8U232AM = 2, FT8U232AM = 2,
}; FT232BM = 3,
} ftdi_chip_type_t;
typedef enum {
enum {
ftdi_sio_b300 = 0, ftdi_sio_b300 = 0,
ftdi_sio_b600 = 1, ftdi_sio_b600 = 1,
ftdi_sio_b1200 = 2, ftdi_sio_b1200 = 2,
...@@ -107,39 +237,12 @@ enum { ...@@ -107,39 +237,12 @@ enum {
ftdi_sio_b38400 = 7, ftdi_sio_b38400 = 7,
ftdi_sio_b57600 = 8, ftdi_sio_b57600 = 8,
ftdi_sio_b115200 = 9 ftdi_sio_b115200 = 9
}; } FTDI_SIO_baudrate_t ;
/*
enum { * The ftdi_8U232AM_xxMHz_byyy constants have been removed. The encoded divisor values
ftdi_8U232AM_12MHz_b300 = 0x09c4, * are calculated internally.
ftdi_8U232AM_12MHz_b600 = 0x04E2, */
ftdi_8U232AM_12MHz_b1200 = 0x0271,
ftdi_8U232AM_12MHz_b2400 = 0x4138,
ftdi_8U232AM_12MHz_b4800 = 0x809c,
ftdi_8U232AM_12MHz_b9600 = 0xc04e,
ftdi_8U232AM_12MHz_b19200 = 0x0027,
ftdi_8U232AM_12MHz_b38400 = 0x4013,
ftdi_8U232AM_12MHz_b57600 = 0x000d,
ftdi_8U232AM_12MHz_b115200 = 0x4006,
ftdi_8U232AM_12MHz_b230400 = 0x8003,
};
/* Apparently all devices are 48MHz */
enum {
ftdi_8U232AM_48MHz_b300 = 0x2710,
ftdi_8U232AM_48MHz_b600 = 0x1388,
ftdi_8U232AM_48MHz_b1200 = 0x09c4,
ftdi_8U232AM_48MHz_b2400 = 0x04e2,
ftdi_8U232AM_48MHz_b4800 = 0x0271,
ftdi_8U232AM_48MHz_b9600 = 0x4138,
ftdi_8U232AM_48MHz_b19200 = 0x809c,
ftdi_8U232AM_48MHz_b38400 = 0xc04e,
ftdi_8U232AM_48MHz_b57600 = 0x0034,
ftdi_8U232AM_48MHz_b115200 = 0x001a,
ftdi_8U232AM_48MHz_b230400 = 0x000d,
ftdi_8U232AM_48MHz_b460800 = 0x4006,
ftdi_8U232AM_48MHz_b921600 = 0x8003,
};
#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA #define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA
#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 #define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40
...@@ -443,6 +546,11 @@ enum { ...@@ -443,6 +546,11 @@ enum {
* B7 Error in RCVR FIFO * B7 Error in RCVR FIFO
* *
*/ */
#define FTDI_RS0_CTS (1 << 4)
#define FTDI_RS0_DSR (1 << 5)
#define FTDI_RS0_RI (1 << 6)
#define FTDI_RS0_RLSD (1 << 7)
#define FTDI_RS_DR 1 #define FTDI_RS_DR 1
#define FTDI_RS_OE (1<<1) #define FTDI_RS_OE (1<<1)
#define FTDI_RS_PE (1<<2) #define FTDI_RS_PE (1<<2)
......
...@@ -94,7 +94,7 @@ static int datafab_read_data(struct us_data *us, ...@@ -94,7 +94,7 @@ static int datafab_read_data(struct us_data *us,
unsigned char *dest, unsigned char *dest,
int use_sg) int use_sg)
{ {
unsigned char command[8] = { 0, 0, 0, 0, 0, 0xE0, 0x20, 0x01 }; unsigned char *command = us->iobuf;
unsigned char *buffer = NULL; unsigned char *buffer = NULL;
unsigned char *ptr; unsigned char *ptr;
unsigned char thistime; unsigned char thistime;
...@@ -116,8 +116,6 @@ static int datafab_read_data(struct us_data *us, ...@@ -116,8 +116,6 @@ static int datafab_read_data(struct us_data *us,
return rc; return rc;
} }
command[5] += (info->lun << 4);
totallen = sectors * info->ssize; totallen = sectors * info->ssize;
do { do {
...@@ -138,10 +136,13 @@ static int datafab_read_data(struct us_data *us, ...@@ -138,10 +136,13 @@ static int datafab_read_data(struct us_data *us,
command[3] = (sector >> 8) & 0xFF; command[3] = (sector >> 8) & 0xFF;
command[4] = (sector >> 16) & 0xFF; command[4] = (sector >> 16) & 0xFF;
command[5] = 0xE0 + (info->lun << 4);
command[5] |= (sector >> 24) & 0x0F; command[5] |= (sector >> 24) & 0x0F;
command[6] = 0x20;
command[7] = 0x01;
// send the read command // send the read command
result = datafab_bulk_write(us, command, sizeof(command)); result = datafab_bulk_write(us, command, 8);
if (result != USB_STOR_XFER_GOOD) if (result != USB_STOR_XFER_GOOD)
goto leave; goto leave;
...@@ -180,8 +181,8 @@ static int datafab_write_data(struct us_data *us, ...@@ -180,8 +181,8 @@ static int datafab_write_data(struct us_data *us,
unsigned char *src, unsigned char *src,
int use_sg) int use_sg)
{ {
unsigned char command[8] = { 0, 0, 0, 0, 0, 0xE0, 0x30, 0x02 }; unsigned char *command = us->iobuf;
unsigned char reply[2] = { 0, 0 }; unsigned char *reply = us->iobuf;
unsigned char *buffer = NULL; unsigned char *buffer = NULL;
unsigned char *ptr; unsigned char *ptr;
unsigned char thistime; unsigned char thistime;
...@@ -202,8 +203,6 @@ static int datafab_write_data(struct us_data *us, ...@@ -202,8 +203,6 @@ static int datafab_write_data(struct us_data *us,
return rc; return rc;
} }
command[5] += (info->lun << 4);
// If we're using scatter-gather, we have to create a new // If we're using scatter-gather, we have to create a new
// buffer to read all of the data in first, since a // buffer to read all of the data in first, since a
// scatter-gather buffer could in theory start in the middle // scatter-gather buffer could in theory start in the middle
...@@ -237,10 +236,13 @@ static int datafab_write_data(struct us_data *us, ...@@ -237,10 +236,13 @@ static int datafab_write_data(struct us_data *us,
command[3] = (sector >> 8) & 0xFF; command[3] = (sector >> 8) & 0xFF;
command[4] = (sector >> 16) & 0xFF; command[4] = (sector >> 16) & 0xFF;
command[5] = 0xE0 + (info->lun << 4);
command[5] |= (sector >> 24) & 0x0F; command[5] |= (sector >> 24) & 0x0F;
command[6] = 0x30;
command[7] = 0x02;
// send the command // send the command
result = datafab_bulk_write(us, command, sizeof(command)); result = datafab_bulk_write(us, command, 8);
if (result != USB_STOR_XFER_GOOD) if (result != USB_STOR_XFER_GOOD)
goto leave; goto leave;
...@@ -250,7 +252,7 @@ static int datafab_write_data(struct us_data *us, ...@@ -250,7 +252,7 @@ static int datafab_write_data(struct us_data *us,
goto leave; goto leave;
// read the result // read the result
result = datafab_bulk_read(us, reply, sizeof(reply)); result = datafab_bulk_read(us, reply, 2);
if (result != USB_STOR_XFER_GOOD) if (result != USB_STOR_XFER_GOOD)
goto leave; goto leave;
...@@ -291,13 +293,19 @@ static int datafab_determine_lun(struct us_data *us, ...@@ -291,13 +293,19 @@ static int datafab_determine_lun(struct us_data *us,
// //
// There might be a better way of doing this? // There might be a better way of doing this?
unsigned char command[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
unsigned char buf[512]; unsigned char *command = us->iobuf;
unsigned char *buf;
int count = 0, rc; int count = 0, rc;
if (!us || !info) if (!us || !info)
return USB_STOR_TRANSPORT_ERROR; return USB_STOR_TRANSPORT_ERROR;
memcpy(command, scommand, 8);
buf = kmalloc(512, GFP_NOIO);
if (!buf)
return USB_STOR_TRANSPORT_ERROR;
US_DEBUGP("datafab_determine_lun: locating...\n"); US_DEBUGP("datafab_determine_lun: locating...\n");
// we'll try 3 times before giving up... // we'll try 3 times before giving up...
...@@ -306,31 +314,41 @@ static int datafab_determine_lun(struct us_data *us, ...@@ -306,31 +314,41 @@ static int datafab_determine_lun(struct us_data *us,
command[5] = 0xa0; command[5] = 0xa0;
rc = datafab_bulk_write(us, command, 8); rc = datafab_bulk_write(us, command, 8);
if (rc != USB_STOR_XFER_GOOD) if (rc != USB_STOR_XFER_GOOD) {
return USB_STOR_TRANSPORT_ERROR; rc = USB_STOR_TRANSPORT_ERROR;
goto leave;
}
rc = datafab_bulk_read(us, buf, sizeof(buf)); rc = datafab_bulk_read(us, buf, 512);
if (rc == USB_STOR_XFER_GOOD) { if (rc == USB_STOR_XFER_GOOD) {
info->lun = 0; info->lun = 0;
return USB_STOR_TRANSPORT_GOOD; rc = USB_STOR_TRANSPORT_GOOD;
goto leave;
} }
command[5] = 0xb0; command[5] = 0xb0;
rc = datafab_bulk_write(us, command, 8); rc = datafab_bulk_write(us, command, 8);
if (rc != USB_STOR_XFER_GOOD) if (rc != USB_STOR_XFER_GOOD) {
return USB_STOR_TRANSPORT_ERROR; rc = USB_STOR_TRANSPORT_ERROR;
goto leave;
}
rc = datafab_bulk_read(us, buf, sizeof(buf)); rc = datafab_bulk_read(us, buf, 512);
if (rc == USB_STOR_XFER_GOOD) { if (rc == USB_STOR_XFER_GOOD) {
info->lun = 1; info->lun = 1;
return USB_STOR_TRANSPORT_GOOD; rc = USB_STOR_TRANSPORT_GOOD;
goto leave;
} }
wait_ms(20); wait_ms(20);
} }
return USB_STOR_TRANSPORT_ERROR; rc = USB_STOR_TRANSPORT_ERROR;
leave:
kfree(buf);
return rc;
} }
static int datafab_id_device(struct us_data *us, static int datafab_id_device(struct us_data *us,
...@@ -340,8 +358,9 @@ static int datafab_id_device(struct us_data *us, ...@@ -340,8 +358,9 @@ static int datafab_id_device(struct us_data *us,
// to the ATA spec, 'Sector Count' isn't used but the Windows driver // to the ATA spec, 'Sector Count' isn't used but the Windows driver
// sets this bit so we do too... // sets this bit so we do too...
// //
unsigned char command[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
unsigned char reply[512]; unsigned char *command = us->iobuf;
unsigned char *reply;
int rc; int rc;
if (!us || !info) if (!us || !info)
...@@ -353,11 +372,18 @@ static int datafab_id_device(struct us_data *us, ...@@ -353,11 +372,18 @@ static int datafab_id_device(struct us_data *us,
return rc; return rc;
} }
memcpy(command, scommand, 8);
reply = kmalloc(512, GFP_NOIO);
if (!reply)
return USB_STOR_TRANSPORT_ERROR;
command[5] += (info->lun << 4); command[5] += (info->lun << 4);
rc = datafab_bulk_write(us, command, 8); rc = datafab_bulk_write(us, command, 8);
if (rc != USB_STOR_XFER_GOOD) if (rc != USB_STOR_XFER_GOOD) {
return USB_STOR_TRANSPORT_ERROR; rc = USB_STOR_TRANSPORT_ERROR;
goto leave;
}
// we'll go ahead and extract the media capacity while we're here... // we'll go ahead and extract the media capacity while we're here...
// //
...@@ -369,10 +395,15 @@ static int datafab_id_device(struct us_data *us, ...@@ -369,10 +395,15 @@ static int datafab_id_device(struct us_data *us,
((u32)(reply[116]) << 16) | ((u32)(reply[116]) << 16) |
((u32)(reply[115]) << 8) | ((u32)(reply[115]) << 8) |
((u32)(reply[114]) ); ((u32)(reply[114]) );
return USB_STOR_TRANSPORT_GOOD; rc = USB_STOR_TRANSPORT_GOOD;
goto leave;
} }
return USB_STOR_TRANSPORT_ERROR; rc = USB_STOR_TRANSPORT_ERROR;
leave:
kfree(reply);
return rc;
} }
...@@ -571,8 +602,7 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us) ...@@ -571,8 +602,7 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
return USB_STOR_TRANSPORT_ERROR; return USB_STOR_TRANSPORT_ERROR;
} }
// don't bother implementing READ_6 or WRITE_6. Just set MODE_XLATE and // don't bother implementing READ_6 or WRITE_6.
// let the usb storage code convert to READ_10/WRITE_10
// //
if (srb->cmnd[0] == READ_10) { if (srb->cmnd[0] == READ_10) {
block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
......
...@@ -44,11 +44,6 @@ static void pdump (void *, int); ...@@ -44,11 +44,6 @@ static void pdump (void *, int);
#define ERR_STAT 0x01 #define ERR_STAT 0x01
#define DRQ_STAT 0x08 #define DRQ_STAT 0x08
struct freecom_udata {
u8 buffer[64]; /* Common command block. */
};
typedef struct freecom_udata *freecom_udata_t;
/* All of the outgoing packets are 64 bytes long. */ /* All of the outgoing packets are 64 bytes long. */
struct freecom_cb_wrap { struct freecom_cb_wrap {
u8 Type; /* Command type. */ u8 Type; /* Command type. */
...@@ -112,9 +107,8 @@ static int ...@@ -112,9 +107,8 @@ static int
freecom_readdata (Scsi_Cmnd *srb, struct us_data *us, freecom_readdata (Scsi_Cmnd *srb, struct us_data *us,
unsigned int ipipe, unsigned int opipe, int count) unsigned int ipipe, unsigned int opipe, int count)
{ {
freecom_udata_t extra = (freecom_udata_t) us->extra;
struct freecom_xfer_wrap *fxfr = struct freecom_xfer_wrap *fxfr =
(struct freecom_xfer_wrap *) extra->buffer; (struct freecom_xfer_wrap *) us->iobuf;
int result; int result;
fxfr->Type = FCM_PACKET_INPUT | 0x00; fxfr->Type = FCM_PACKET_INPUT | 0x00;
...@@ -147,9 +141,8 @@ static int ...@@ -147,9 +141,8 @@ static int
freecom_writedata (Scsi_Cmnd *srb, struct us_data *us, freecom_writedata (Scsi_Cmnd *srb, struct us_data *us,
int unsigned ipipe, unsigned int opipe, int count) int unsigned ipipe, unsigned int opipe, int count)
{ {
freecom_udata_t extra = (freecom_udata_t) us->extra;
struct freecom_xfer_wrap *fxfr = struct freecom_xfer_wrap *fxfr =
(struct freecom_xfer_wrap *) extra->buffer; (struct freecom_xfer_wrap *) us->iobuf;
int result; int result;
fxfr->Type = FCM_PACKET_OUTPUT | 0x00; fxfr->Type = FCM_PACKET_OUTPUT | 0x00;
...@@ -190,12 +183,9 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us) ...@@ -190,12 +183,9 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
int result; int result;
unsigned int partial; unsigned int partial;
int length; int length;
freecom_udata_t extra;
extra = (freecom_udata_t) us->extra; fcb = (struct freecom_cb_wrap *) us->iobuf;
fst = (struct freecom_status *) us->iobuf;
fcb = (struct freecom_cb_wrap *) extra->buffer;
fst = (struct freecom_status *) extra->buffer;
US_DEBUGP("Freecom TRANSPORT STARTED\n"); US_DEBUGP("Freecom TRANSPORT STARTED\n");
...@@ -386,18 +376,11 @@ int ...@@ -386,18 +376,11 @@ int
freecom_init (struct us_data *us) freecom_init (struct us_data *us)
{ {
int result; int result;
char buffer[33]; char *buffer = us->iobuf;
/* Allocate a buffer for us. The upper usb transport code will /* The DMA-mapped I/O buffer is 64 bytes long, just right for
* free this for us when cleaning up. */ * all our packets. No need to allocate any extra buffer space.
if (us->extra == NULL) { */
us->extra = kmalloc (sizeof (struct freecom_udata),
GFP_KERNEL);
if (us->extra == NULL) {
US_DEBUGP("Out of memory\n");
return USB_STOR_TRANSPORT_ERROR;
}
}
result = usb_stor_control_msg(us, us->recv_ctrl_pipe, result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
0x4c, 0xc0, 0x4346, 0x0, buffer, 0x20, 3*HZ); 0x4c, 0xc0, 0x4346, 0x0, buffer, 0x20, 3*HZ);
......
...@@ -47,38 +47,27 @@ ...@@ -47,38 +47,27 @@
* mode */ * mode */
int usb_stor_euscsi_init(struct us_data *us) int usb_stor_euscsi_init(struct us_data *us)
{ {
unsigned char data = 0x1;
int result; int result;
US_DEBUGP("Attempting to init eUSCSI bridge...\n"); US_DEBUGP("Attempting to init eUSCSI bridge...\n");
us->iobuf[0] = 0x1;
result = usb_stor_control_msg(us, us->send_ctrl_pipe, result = usb_stor_control_msg(us, us->send_ctrl_pipe,
0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR, 0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR,
0x01, 0x0, &data, 0x1, 5*HZ); 0x01, 0x0, us->iobuf, 0x1, 5*HZ);
US_DEBUGP("-- result is %d\n", result); US_DEBUGP("-- result is %d\n", result);
US_DEBUGP("-- data afterwards is %d\n", data); US_DEBUGP("-- data afterwards is %d\n", us->iobuf[0]);
return 0; return 0;
} }
/* This function is required to activate all four slots on the UCR-61S2B /* This function is required to activate all four slots on the UCR-61S2B
* flash reader */ * flash reader */
int usb_stor_ucr61s2b_init(struct us_data *us) int usb_stor_ucr61s2b_init(struct us_data *us)
{ {
struct bulk_cb_wrap *bcb; struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap*) us->iobuf;
struct bulk_cs_wrap *bcs; struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap*) us->iobuf;
int res, partial; int res, partial;
bcb = kmalloc(sizeof *bcb, in_interrupt() ? GFP_ATOMIC : GFP_NOIO);
if (!bcb) {
return(-1);
}
bcs = kmalloc(sizeof *bcs, in_interrupt() ? GFP_ATOMIC : GFP_NOIO);
if (!bcs) {
kfree(bcb);
return(-1);
}
US_DEBUGP("Sending UCR-61S2B initialization packet...\n"); US_DEBUGP("Sending UCR-61S2B initialization packet...\n");
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
...@@ -91,19 +80,12 @@ int usb_stor_ucr61s2b_init(struct us_data *us) ...@@ -91,19 +80,12 @@ int usb_stor_ucr61s2b_init(struct us_data *us)
res = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcb, res = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcb,
US_BULK_CB_WRAP_LEN, &partial); US_BULK_CB_WRAP_LEN, &partial);
US_DEBUGP("-- result is %d\n", res); if(res)
kfree(bcb); return res;
if(res) {
kfree(bcs);
return(res);
}
US_DEBUGP("Getting status packet...\n");
res = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs, res = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs,
US_BULK_CS_WRAP_LEN, &partial); US_BULK_CS_WRAP_LEN, &partial);
US_DEBUGP("-- result of status read is %d\n", res);
kfree(bcs);
return(res ? -1 : 0); return (res ? -1 : 0);
} }
...@@ -90,12 +90,6 @@ UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, ...@@ -90,12 +90,6 @@ UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100,
US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ),
#endif #endif
/* Made with the help of Edd Dumbill <edd@usefulinc.com> */
UNUSUAL_DEV( 0x0451, 0x5409, 0x0001, 0x0001,
"Frontier Labs",
"Nex II Digital",
US_SC_SCSI, US_PR_BULK, NULL, 0),
/* Patch submitted by Philipp Friedrich <philipp@void.at> */ /* Patch submitted by Philipp Friedrich <philipp@void.at> */
UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100,
"Kyocera", "Kyocera",
...@@ -276,7 +270,7 @@ UNUSUAL_DEV( 0x054c, 0x002e, 0x0106, 0x0310, ...@@ -276,7 +270,7 @@ UNUSUAL_DEV( 0x054c, 0x002e, 0x0106, 0x0310,
UNUSUAL_DEV( 0x054c, 0x0032, 0x0000, 0x9999, UNUSUAL_DEV( 0x054c, 0x0032, 0x0000, 0x9999,
"Sony", "Sony",
"Memorystick MSC-U01N", "Memorystick MSC-U01N",
US_SC_UFI, US_PR_CB, NULL, US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_SINGLE_LUN ), US_FL_SINGLE_LUN ),
UNUSUAL_DEV( 0x054c, 0x0069, 0x0000, 0x9999, UNUSUAL_DEV( 0x054c, 0x0069, 0x0000, 0x9999,
...@@ -301,7 +295,7 @@ UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299, ...@@ -301,7 +295,7 @@ UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299,
UNUSUAL_DEV( 0x057b, 0x0000, 0x0300, 0x9999, UNUSUAL_DEV( 0x057b, 0x0000, 0x0300, 0x9999,
"Y-E Data", "Y-E Data",
"Flashbuster-U", "Flashbuster-U",
US_SC_UFI, US_PR_CBI, NULL, US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_SINGLE_LUN), US_FL_SINGLE_LUN),
UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200, UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200,
......
...@@ -646,9 +646,6 @@ static void skel_disconnect(struct usb_interface *interface) ...@@ -646,9 +646,6 @@ static void skel_disconnect(struct usb_interface *interface)
dev = usb_get_intfdata (interface); dev = usb_get_intfdata (interface);
usb_set_intfdata (interface, NULL); usb_set_intfdata (interface, NULL);
if (!dev)
return;
down (&dev->sem); down (&dev->sem);
/* disable open() */ /* disable open() */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment