mirror of
https://github.com/torvalds/linux.git
synced 2024-11-02 10:11:36 +00:00
USB: ipaq: reimplement using generic framework
Kill custom fifo, read and write implementations (single-urb and fifo, but still maintained list of 256*256b urb buffers per port). Compile-only tested. Signed-off-by: Johan Hovold <jhovold@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
695aaae684
commit
6b6962f9c4
@ -56,7 +56,6 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/serial.h>
|
||||
#include "ipaq.h"
|
||||
|
||||
#define KP_RETRIES 100
|
||||
|
||||
@ -64,7 +63,7 @@
|
||||
* Version Information
|
||||
*/
|
||||
|
||||
#define DRIVER_VERSION "v0.5"
|
||||
#define DRIVER_VERSION "v1.0"
|
||||
#define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
|
||||
#define DRIVER_DESC "USB PocketPC PDA driver"
|
||||
|
||||
@ -76,20 +75,8 @@ static int initial_wait;
|
||||
/* Function prototypes for an ipaq */
|
||||
static int ipaq_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port);
|
||||
static void ipaq_close(struct usb_serial_port *port);
|
||||
static int ipaq_calc_num_ports(struct usb_serial *serial);
|
||||
static int ipaq_startup(struct usb_serial *serial);
|
||||
static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
static int ipaq_write_bulk(struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
static void ipaq_write_gather(struct usb_serial_port *port);
|
||||
static void ipaq_read_bulk_callback(struct urb *urb);
|
||||
static void ipaq_write_bulk_callback(struct urb *urb);
|
||||
static int ipaq_write_room(struct tty_struct *tty);
|
||||
static int ipaq_chars_in_buffer(struct tty_struct *tty);
|
||||
static void ipaq_destroy_lists(struct usb_serial_port *port);
|
||||
|
||||
|
||||
static struct usb_device_id ipaq_id_table [] = {
|
||||
/* The first entry is a placeholder for the insmod-specified device */
|
||||
@ -558,7 +545,7 @@ static struct usb_driver ipaq_driver = {
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = ipaq_id_table,
|
||||
.no_dynamic_id = 1,
|
||||
.no_dynamic_id = 1,
|
||||
};
|
||||
|
||||
|
||||
@ -569,67 +556,24 @@ static struct usb_serial_driver ipaq_device = {
|
||||
.name = "ipaq",
|
||||
},
|
||||
.description = "PocketPC PDA",
|
||||
.usb_driver = &ipaq_driver,
|
||||
.usb_driver = &ipaq_driver,
|
||||
.id_table = ipaq_id_table,
|
||||
.bulk_in_size = URBDATA_SIZE,
|
||||
.bulk_out_size = URBDATA_SIZE,
|
||||
.bulk_in_size = 256,
|
||||
.bulk_out_size = 256,
|
||||
.open = ipaq_open,
|
||||
.close = ipaq_close,
|
||||
.attach = ipaq_startup,
|
||||
.calc_num_ports = ipaq_calc_num_ports,
|
||||
.write = ipaq_write,
|
||||
.write_room = ipaq_write_room,
|
||||
.chars_in_buffer = ipaq_chars_in_buffer,
|
||||
.read_bulk_callback = ipaq_read_bulk_callback,
|
||||
.write_bulk_callback = ipaq_write_bulk_callback,
|
||||
};
|
||||
|
||||
static spinlock_t write_list_lock;
|
||||
static int bytes_in;
|
||||
static int bytes_out;
|
||||
|
||||
static int ipaq_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct ipaq_private *priv;
|
||||
struct ipaq_packet *pkt;
|
||||
int i, result = 0;
|
||||
int result = 0;
|
||||
int retries = connect_retries;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
bytes_in = 0;
|
||||
bytes_out = 0;
|
||||
priv = kmalloc(sizeof(struct ipaq_private), GFP_KERNEL);
|
||||
if (priv == NULL) {
|
||||
dev_err(&port->dev, "%s - Out of memory\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
usb_set_serial_port_data(port, priv);
|
||||
priv->active = 0;
|
||||
priv->queue_len = 0;
|
||||
priv->free_len = 0;
|
||||
INIT_LIST_HEAD(&priv->queue);
|
||||
INIT_LIST_HEAD(&priv->freelist);
|
||||
|
||||
for (i = 0; i < URBDATA_QUEUE_MAX / PACKET_SIZE; i++) {
|
||||
pkt = kmalloc(sizeof(struct ipaq_packet), GFP_KERNEL);
|
||||
if (pkt == NULL)
|
||||
goto enomem;
|
||||
|
||||
pkt->data = kmalloc(PACKET_SIZE, GFP_KERNEL);
|
||||
if (pkt->data == NULL) {
|
||||
kfree(pkt);
|
||||
goto enomem;
|
||||
}
|
||||
pkt->len = 0;
|
||||
pkt->written = 0;
|
||||
INIT_LIST_HEAD(&pkt->list);
|
||||
list_add(&pkt->list, &priv->freelist);
|
||||
priv->free_len += PACKET_SIZE;
|
||||
}
|
||||
|
||||
msleep(1000*initial_wait);
|
||||
|
||||
/*
|
||||
@ -639,7 +583,6 @@ static int ipaq_open(struct tty_struct *tty,
|
||||
* through. Since this has a reasonably high failure rate, we retry
|
||||
* several times.
|
||||
*/
|
||||
|
||||
while (retries--) {
|
||||
result = usb_control_msg(serial->dev,
|
||||
usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
|
||||
@ -649,269 +592,15 @@ static int ipaq_open(struct tty_struct *tty,
|
||||
|
||||
msleep(1000);
|
||||
}
|
||||
|
||||
if (!retries && result) {
|
||||
dev_err(&port->dev, "%s - failed doing control urb, error %d\n", __func__, result);
|
||||
goto error;
|
||||
dev_err(&port->dev, "%s - failed doing control urb, error %d\n",
|
||||
__func__, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Start reading from the device */
|
||||
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,
|
||||
ipaq_read_bulk_callback, port);
|
||||
|
||||
result = usb_submit_urb(port->read_urb, GFP_KERNEL);
|
||||
if (result) {
|
||||
dev_err(&port->dev,
|
||||
"%s - failed submitting read urb, error %d\n",
|
||||
__func__, result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
enomem:
|
||||
result = -ENOMEM;
|
||||
dev_err(&port->dev, "%s - Out of memory\n", __func__);
|
||||
error:
|
||||
ipaq_destroy_lists(port);
|
||||
kfree(priv);
|
||||
return result;
|
||||
return usb_serial_generic_open(tty, port);
|
||||
}
|
||||
|
||||
|
||||
static void ipaq_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct ipaq_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
/*
|
||||
* shut down bulk read and write
|
||||
*/
|
||||
usb_kill_urb(port->write_urb);
|
||||
usb_kill_urb(port->read_urb);
|
||||
ipaq_destroy_lists(port);
|
||||
kfree(priv);
|
||||
usb_set_serial_port_data(port, NULL);
|
||||
|
||||
/* Uncomment the following line if you want to see some statistics
|
||||
* in your syslog */
|
||||
/* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */
|
||||
}
|
||||
|
||||
static void ipaq_read_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct usb_serial_port *port = urb->context;
|
||||
struct tty_struct *tty;
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
int result;
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
if (status) {
|
||||
dbg("%s - nonzero read bulk status received: %d",
|
||||
__func__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
usb_serial_debug_data(debug, &port->dev, __func__,
|
||||
urb->actual_length, data);
|
||||
|
||||
tty = tty_port_tty_get(&port->port);
|
||||
if (tty && urb->actual_length) {
|
||||
tty_insert_flip_string(tty, data, urb->actual_length);
|
||||
tty_flip_buffer_push(tty);
|
||||
bytes_in += urb->actual_length;
|
||||
}
|
||||
tty_kref_put(tty);
|
||||
|
||||
/* Continue trying to always read */
|
||||
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
|
||||
usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress),
|
||||
port->read_urb->transfer_buffer,
|
||||
port->read_urb->transfer_buffer_length,
|
||||
ipaq_read_bulk_callback, port);
|
||||
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dev_err(&port->dev,
|
||||
"%s - failed resubmitting read urb, error %d\n",
|
||||
__func__, result);
|
||||
return;
|
||||
}
|
||||
|
||||
static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count)
|
||||
{
|
||||
const unsigned char *current_position = buf;
|
||||
int bytes_sent = 0;
|
||||
int transfer_size;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
while (count > 0) {
|
||||
transfer_size = min(count, PACKET_SIZE);
|
||||
if (ipaq_write_bulk(port, current_position, transfer_size))
|
||||
break;
|
||||
current_position += transfer_size;
|
||||
bytes_sent += transfer_size;
|
||||
count -= transfer_size;
|
||||
bytes_out += transfer_size;
|
||||
}
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
static int ipaq_write_bulk(struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count)
|
||||
{
|
||||
struct ipaq_private *priv = usb_get_serial_port_data(port);
|
||||
struct ipaq_packet *pkt = NULL;
|
||||
int result = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (priv->free_len <= 0) {
|
||||
dbg("%s - we're stuffed", __func__);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&write_list_lock, flags);
|
||||
if (!list_empty(&priv->freelist)) {
|
||||
pkt = list_entry(priv->freelist.next, struct ipaq_packet, list);
|
||||
list_del(&pkt->list);
|
||||
priv->free_len -= PACKET_SIZE;
|
||||
}
|
||||
spin_unlock_irqrestore(&write_list_lock, flags);
|
||||
if (pkt == NULL) {
|
||||
dbg("%s - we're stuffed", __func__);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
memcpy(pkt->data, buf, count);
|
||||
usb_serial_debug_data(debug, &port->dev, __func__, count, pkt->data);
|
||||
|
||||
pkt->len = count;
|
||||
pkt->written = 0;
|
||||
spin_lock_irqsave(&write_list_lock, flags);
|
||||
list_add_tail(&pkt->list, &priv->queue);
|
||||
priv->queue_len += count;
|
||||
if (priv->active == 0) {
|
||||
priv->active = 1;
|
||||
ipaq_write_gather(port);
|
||||
spin_unlock_irqrestore(&write_list_lock, flags);
|
||||
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dev_err(&port->dev,
|
||||
"%s - failed submitting write urb, error %d\n",
|
||||
__func__, result);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&write_list_lock, flags);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ipaq_write_gather(struct usb_serial_port *port)
|
||||
{
|
||||
struct ipaq_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_serial *serial = port->serial;
|
||||
int count, room;
|
||||
struct ipaq_packet *pkt, *tmp;
|
||||
struct urb *urb = port->write_urb;
|
||||
|
||||
room = URBDATA_SIZE;
|
||||
list_for_each_entry_safe(pkt, tmp, &priv->queue, list) {
|
||||
count = min(room, (int)(pkt->len - pkt->written));
|
||||
memcpy(urb->transfer_buffer + (URBDATA_SIZE - room),
|
||||
pkt->data + pkt->written, count);
|
||||
room -= count;
|
||||
pkt->written += count;
|
||||
priv->queue_len -= count;
|
||||
if (pkt->written == pkt->len) {
|
||||
list_move(&pkt->list, &priv->freelist);
|
||||
priv->free_len += PACKET_SIZE;
|
||||
}
|
||||
if (room == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
count = URBDATA_SIZE - room;
|
||||
usb_fill_bulk_urb(port->write_urb, serial->dev,
|
||||
usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
|
||||
port->write_urb->transfer_buffer, count,
|
||||
ipaq_write_bulk_callback, port);
|
||||
return;
|
||||
}
|
||||
|
||||
static void ipaq_write_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct usb_serial_port *port = urb->context;
|
||||
struct ipaq_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned long flags;
|
||||
int result;
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
if (status) {
|
||||
dbg("%s - nonzero write bulk status received: %d",
|
||||
__func__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&write_list_lock, flags);
|
||||
if (!list_empty(&priv->queue)) {
|
||||
ipaq_write_gather(port);
|
||||
spin_unlock_irqrestore(&write_list_lock, flags);
|
||||
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dev_err(&port->dev,
|
||||
"%s - failed submitting write urb, error %d\n",
|
||||
__func__, result);
|
||||
} else {
|
||||
priv->active = 0;
|
||||
spin_unlock_irqrestore(&write_list_lock, flags);
|
||||
}
|
||||
|
||||
usb_serial_port_softint(port);
|
||||
}
|
||||
|
||||
static int ipaq_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct ipaq_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
dbg("%s - freelen %d", __func__, priv->free_len);
|
||||
return priv->free_len;
|
||||
}
|
||||
|
||||
static int ipaq_chars_in_buffer(struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct ipaq_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
dbg("%s - queuelen %d", __func__, priv->queue_len);
|
||||
return priv->queue_len;
|
||||
}
|
||||
|
||||
static void ipaq_destroy_lists(struct usb_serial_port *port)
|
||||
{
|
||||
struct ipaq_private *priv = usb_get_serial_port_data(port);
|
||||
struct ipaq_packet *pkt, *tmp;
|
||||
|
||||
list_for_each_entry_safe(pkt, tmp, &priv->queue, list) {
|
||||
kfree(pkt->data);
|
||||
kfree(pkt);
|
||||
}
|
||||
list_for_each_entry_safe(pkt, tmp, &priv->freelist, list) {
|
||||
kfree(pkt->data);
|
||||
kfree(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int ipaq_calc_num_ports(struct usb_serial *serial)
|
||||
{
|
||||
/*
|
||||
@ -970,7 +659,6 @@ static int ipaq_startup(struct usb_serial *serial)
|
||||
static int __init ipaq_init(void)
|
||||
{
|
||||
int retval;
|
||||
spin_lock_init(&write_list_lock);
|
||||
retval = usb_serial_register(&ipaq_device);
|
||||
if (retval)
|
||||
goto failed_usb_serial_register;
|
||||
@ -991,7 +679,6 @@ failed_usb_serial_register:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static void __exit ipaq_exit(void)
|
||||
{
|
||||
usb_deregister(&ipaq_driver);
|
||||
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* USB Compaq iPAQ driver
|
||||
*
|
||||
* Copyright (C) 2001 - 2002
|
||||
* Ganesh Varadarajan <ganesh@veritas.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_SERIAL_IPAQ_H
|
||||
#define __LINUX_USB_SERIAL_IPAQ_H
|
||||
|
||||
/*
|
||||
* Since we can't queue our bulk write urbs (don't know why - it just
|
||||
* doesn't work), we can send down only one write urb at a time. The simplistic
|
||||
* approach taken by the generic usbserial driver will work, but it's not good
|
||||
* for performance. Therefore, we buffer upto URBDATA_QUEUE_MAX bytes of write
|
||||
* requests coming from the line discipline. This is done by chaining them
|
||||
* in lists of struct ipaq_packet, each packet holding a maximum of
|
||||
* PACKET_SIZE bytes.
|
||||
*
|
||||
* ipaq_write() can be called from bottom half context; hence we can't
|
||||
* allocate memory for packets there. So we initialize a pool of packets at
|
||||
* the first open and maintain a freelist.
|
||||
*
|
||||
* The value of PACKET_SIZE was empirically determined by
|
||||
* checking the maximum write sizes sent down by the ppp ldisc.
|
||||
* URBDATA_QUEUE_MAX is set to 64K, which is the maximum TCP window size.
|
||||
*/
|
||||
|
||||
struct ipaq_packet {
|
||||
char *data;
|
||||
size_t len;
|
||||
size_t written;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct ipaq_private {
|
||||
int active;
|
||||
int queue_len;
|
||||
int free_len;
|
||||
struct list_head queue;
|
||||
struct list_head freelist;
|
||||
};
|
||||
|
||||
#define URBDATA_SIZE 4096
|
||||
#define URBDATA_QUEUE_MAX (64 * 1024)
|
||||
#define PACKET_SIZE 256
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user