tty: cdc_acm add krefs

Now we have a port structure begin using the fields and kref counts

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Alan Cox 2009-06-11 12:36:09 +01:00 committed by Linus Torvalds
parent b39933fbd3
commit 10077d4a66
2 changed files with 98 additions and 68 deletions

View File

@ -62,7 +62,7 @@
#include <linux/tty_flip.h> #include <linux/tty_flip.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <asm/uaccess.h> #include <linux/uaccess.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb/cdc.h> #include <linux/usb/cdc.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
@ -87,7 +87,7 @@ static struct acm *acm_table[ACM_TTY_MINORS];
static DEFINE_MUTEX(open_mutex); static DEFINE_MUTEX(open_mutex);
#define ACM_READY(acm) (acm && acm->dev && acm->used) #define ACM_READY(acm) (acm && acm->dev && acm->port.count)
static const struct tty_port_operations acm_port_ops = { static const struct tty_port_operations acm_port_ops = {
}; };
@ -265,6 +265,7 @@ static void acm_ctrl_irq(struct urb *urb)
{ {
struct acm *acm = urb->context; struct acm *acm = urb->context;
struct usb_cdc_notification *dr = urb->transfer_buffer; struct usb_cdc_notification *dr = urb->transfer_buffer;
struct tty_struct *tty;
unsigned char *data; unsigned char *data;
int newctrl; int newctrl;
int retval; int retval;
@ -297,12 +298,16 @@ static void acm_ctrl_irq(struct urb *urb)
break; break;
case USB_CDC_NOTIFY_SERIAL_STATE: case USB_CDC_NOTIFY_SERIAL_STATE:
tty = tty_port_tty_get(&acm->port);
newctrl = get_unaligned_le16(data); newctrl = get_unaligned_le16(data);
if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { if (tty) {
dbg("calling hangup"); if (!acm->clocal &&
tty_hangup(acm->tty); (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
dbg("calling hangup");
tty_hangup(tty);
}
tty_kref_put(tty);
} }
acm->ctrlin = newctrl; acm->ctrlin = newctrl;
@ -374,15 +379,14 @@ static void acm_rx_tasklet(unsigned long _acm)
{ {
struct acm *acm = (void *)_acm; struct acm *acm = (void *)_acm;
struct acm_rb *buf; struct acm_rb *buf;
struct tty_struct *tty = acm->tty; struct tty_struct *tty;
struct acm_ru *rcv; struct acm_ru *rcv;
unsigned long flags; unsigned long flags;
unsigned char throttled; unsigned char throttled;
dbg("Entering acm_rx_tasklet"); dbg("Entering acm_rx_tasklet");
if (!ACM_READY(acm)) if (!ACM_READY(acm)) {
{
dbg("acm_rx_tasklet: ACM not ready"); dbg("acm_rx_tasklet: ACM not ready");
return; return;
} }
@ -390,12 +394,13 @@ static void acm_rx_tasklet(unsigned long _acm)
spin_lock_irqsave(&acm->throttle_lock, flags); spin_lock_irqsave(&acm->throttle_lock, flags);
throttled = acm->throttle; throttled = acm->throttle;
spin_unlock_irqrestore(&acm->throttle_lock, flags); spin_unlock_irqrestore(&acm->throttle_lock, flags);
if (throttled) if (throttled) {
{
dbg("acm_rx_tasklet: throttled"); dbg("acm_rx_tasklet: throttled");
return; return;
} }
tty = tty_port_tty_get(&acm->port);
next_buffer: next_buffer:
spin_lock_irqsave(&acm->read_lock, flags); spin_lock_irqsave(&acm->read_lock, flags);
if (list_empty(&acm->filled_read_bufs)) { if (list_empty(&acm->filled_read_bufs)) {
@ -409,20 +414,22 @@ next_buffer:
dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
tty_buffer_request_room(tty, buf->size); if (tty) {
spin_lock_irqsave(&acm->throttle_lock, flags); spin_lock_irqsave(&acm->throttle_lock, flags);
throttled = acm->throttle; throttled = acm->throttle;
spin_unlock_irqrestore(&acm->throttle_lock, flags); spin_unlock_irqrestore(&acm->throttle_lock, flags);
if (!throttled) if (!throttled) {
tty_insert_flip_string(tty, buf->base, buf->size); tty_buffer_request_room(tty, buf->size);
tty_flip_buffer_push(tty); tty_insert_flip_string(tty, buf->base, buf->size);
tty_flip_buffer_push(tty);
if (throttled) { } else {
dbg("Throttling noticed"); tty_kref_put(tty);
spin_lock_irqsave(&acm->read_lock, flags); dbg("Throttling noticed");
list_add(&buf->list, &acm->filled_read_bufs); spin_lock_irqsave(&acm->read_lock, flags);
spin_unlock_irqrestore(&acm->read_lock, flags); list_add(&buf->list, &acm->filled_read_bufs);
return; spin_unlock_irqrestore(&acm->read_lock, flags);
return;
}
} }
spin_lock_irqsave(&acm->read_lock, flags); spin_lock_irqsave(&acm->read_lock, flags);
@ -431,6 +438,8 @@ next_buffer:
goto next_buffer; goto next_buffer;
urbs: urbs:
tty_kref_put(tty);
while (!list_empty(&acm->spare_read_bufs)) { while (!list_empty(&acm->spare_read_bufs)) {
spin_lock_irqsave(&acm->read_lock, flags); spin_lock_irqsave(&acm->read_lock, flags);
if (list_empty(&acm->spare_read_urbs)) { if (list_empty(&acm->spare_read_urbs)) {
@ -502,11 +511,14 @@ static void acm_write_bulk(struct urb *urb)
static void acm_softint(struct work_struct *work) static void acm_softint(struct work_struct *work)
{ {
struct acm *acm = container_of(work, struct acm, work); struct acm *acm = container_of(work, struct acm, work);
struct tty_struct *tty;
dev_vdbg(&acm->data->dev, "tx work\n"); dev_vdbg(&acm->data->dev, "tx work\n");
if (!ACM_READY(acm)) if (!ACM_READY(acm))
return; return;
tty_wakeup(acm->tty); tty = tty_port_tty_get(&acm->port);
tty_wakeup(tty);
tty_kref_put(tty);
} }
static void acm_waker(struct work_struct *waker) static void acm_waker(struct work_struct *waker)
@ -546,8 +558,9 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
rv = 0; rv = 0;
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
tty->driver_data = acm; tty->driver_data = acm;
acm->tty = tty; tty_port_tty_set(&acm->port, tty);
if (usb_autopm_get_interface(acm->control) < 0) if (usb_autopm_get_interface(acm->control) < 0)
goto early_bail; goto early_bail;
@ -555,11 +568,10 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
acm->control->needs_remote_wakeup = 1; acm->control->needs_remote_wakeup = 1;
mutex_lock(&acm->mutex); mutex_lock(&acm->mutex);
if (acm->used++) { if (acm->port.count++) {
usb_autopm_put_interface(acm->control); usb_autopm_put_interface(acm->control);
goto done; goto done;
} }
acm->ctrlurb->dev = acm->dev; acm->ctrlurb->dev = acm->dev;
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
@ -570,6 +582,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
(acm->ctrl_caps & USB_CDC_CAP_LINE)) (acm->ctrl_caps & USB_CDC_CAP_LINE))
goto full_bailout; goto full_bailout;
usb_autopm_put_interface(acm->control); usb_autopm_put_interface(acm->control);
INIT_LIST_HEAD(&acm->spare_read_urbs); INIT_LIST_HEAD(&acm->spare_read_urbs);
@ -585,7 +598,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
acm->throttle = 0; acm->throttle = 0;
tasklet_schedule(&acm->urb_task); tasklet_schedule(&acm->urb_task);
rv = tty_port_block_til_ready(&acm->port, tty, filp);
done: done:
mutex_unlock(&acm->mutex); mutex_unlock(&acm->mutex);
err_out: err_out:
@ -596,16 +609,17 @@ full_bailout:
usb_kill_urb(acm->ctrlurb); usb_kill_urb(acm->ctrlurb);
bail_out: bail_out:
usb_autopm_put_interface(acm->control); usb_autopm_put_interface(acm->control);
acm->used--; acm->port.count--;
mutex_unlock(&acm->mutex); mutex_unlock(&acm->mutex);
early_bail: early_bail:
mutex_unlock(&open_mutex); mutex_unlock(&open_mutex);
tty_port_tty_set(&acm->port, NULL);
return -EIO; return -EIO;
} }
static void acm_tty_unregister(struct acm *acm) static void acm_tty_unregister(struct acm *acm)
{ {
int i,nr; int i, nr;
nr = acm->rx_buflimit; nr = acm->rx_buflimit;
tty_unregister_device(acm_tty_driver, acm->minor); tty_unregister_device(acm_tty_driver, acm->minor);
@ -622,37 +636,51 @@ static void acm_tty_unregister(struct acm *acm)
static int acm_tty_chars_in_buffer(struct tty_struct *tty); static int acm_tty_chars_in_buffer(struct tty_struct *tty);
static void acm_port_down(struct acm *acm, int drain)
{
int i, nr = acm->rx_buflimit;
mutex_lock(&open_mutex);
if (acm->dev) {
usb_autopm_get_interface(acm->control);
acm_set_control(acm, acm->ctrlout = 0);
/* try letting the last writes drain naturally */
if (drain) {
wait_event_interruptible_timeout(acm->drain_wait,
(ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
ACM_CLOSE_TIMEOUT * HZ);
}
usb_kill_urb(acm->ctrlurb);
for (i = 0; i < ACM_NW; i++)
usb_kill_urb(acm->wb[i].urb);
for (i = 0; i < nr; i++)
usb_kill_urb(acm->ru[i].urb);
acm->control->needs_remote_wakeup = 0;
usb_autopm_put_interface(acm->control);
}
mutex_unlock(&open_mutex);
}
static void acm_tty_hangup(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
tty_port_hangup(&acm->port);
acm_port_down(acm, 0);
}
static void acm_tty_close(struct tty_struct *tty, struct file *filp) static void acm_tty_close(struct tty_struct *tty, struct file *filp)
{ {
struct acm *acm = tty->driver_data; struct acm *acm = tty->driver_data;
int i,nr;
if (!acm || !acm->used) /* Perform the closing process and see if we need to do the hardware
shutdown */
if (tty_port_close_start(&acm->port, tty, filp) == 0)
return; return;
acm_port_down(acm, 0);
nr = acm->rx_buflimit; tty_port_close_end(&acm->port, tty);
mutex_lock(&open_mutex); mutex_lock(&open_mutex);
if (!--acm->used) { tty_port_tty_set(&acm->port, NULL);
if (acm->dev) { if (!acm->dev)
usb_autopm_get_interface(acm->control); acm_tty_unregister(acm);
acm_set_control(acm, acm->ctrlout = 0);
/* try letting the last writes drain naturally */
wait_event_interruptible_timeout(acm->drain_wait,
(ACM_NW == acm_wb_is_avail(acm))
|| !acm->dev,
ACM_CLOSE_TIMEOUT * HZ);
usb_kill_urb(acm->ctrlurb);
for (i = 0; i < ACM_NW; i++)
usb_kill_urb(acm->wb[i].urb);
for (i = 0; i < nr; i++)
usb_kill_urb(acm->ru[i].urb);
acm->control->needs_remote_wakeup = 0;
usb_autopm_put_interface(acm->control);
} else
acm_tty_unregister(acm);
}
mutex_unlock(&open_mutex); mutex_unlock(&open_mutex);
} }
@ -885,8 +913,8 @@ static int acm_write_buffers_alloc(struct acm *acm)
return 0; return 0;
} }
static int acm_probe (struct usb_interface *intf, static int acm_probe(struct usb_interface *intf,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct usb_cdc_union_desc *union_header = NULL; struct usb_cdc_union_desc *union_header = NULL;
struct usb_cdc_country_functional_desc *cfd = NULL; struct usb_cdc_country_functional_desc *cfd = NULL;
@ -1232,6 +1260,7 @@ static void acm_disconnect(struct usb_interface *intf)
{ {
struct acm *acm = usb_get_intfdata(intf); struct acm *acm = usb_get_intfdata(intf);
struct usb_device *usb_dev = interface_to_usbdev(intf); struct usb_device *usb_dev = interface_to_usbdev(intf);
struct tty_struct *tty;
/* sibling interface is already cleaning up */ /* sibling interface is already cleaning up */
if (!acm) if (!acm)
@ -1258,16 +1287,18 @@ static void acm_disconnect(struct usb_interface *intf)
usb_driver_release_interface(&acm_driver, intf == acm->control ? usb_driver_release_interface(&acm_driver, intf == acm->control ?
acm->data : acm->control); acm->data : acm->control);
if (!acm->used) { if (acm->port.count == 0) {
acm_tty_unregister(acm); acm_tty_unregister(acm);
mutex_unlock(&open_mutex); mutex_unlock(&open_mutex);
return; return;
} }
mutex_unlock(&open_mutex); mutex_unlock(&open_mutex);
tty = tty_port_tty_get(&acm->port);
if (acm->tty) if (tty) {
tty_hangup(acm->tty); tty_hangup(tty);
tty_kref_put(tty);
}
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
@ -1302,7 +1333,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
*/ */
mutex_lock(&acm->mutex); mutex_lock(&acm->mutex);
if (acm->used) if (acm->port.count)
stop_data_traffic(acm); stop_data_traffic(acm);
mutex_unlock(&acm->mutex); mutex_unlock(&acm->mutex);
@ -1324,7 +1355,7 @@ static int acm_resume(struct usb_interface *intf)
return 0; return 0;
mutex_lock(&acm->mutex); mutex_lock(&acm->mutex);
if (acm->used) { if (acm->port.count) {
rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
if (rv < 0) if (rv < 0)
goto err_out; goto err_out;
@ -1434,6 +1465,7 @@ static struct usb_driver acm_driver = {
static const struct tty_operations acm_ops = { static const struct tty_operations acm_ops = {
.open = acm_tty_open, .open = acm_tty_open,
.close = acm_tty_close, .close = acm_tty_close,
.hangup = acm_tty_hangup,
.write = acm_tty_write, .write = acm_tty_write,
.write_room = acm_tty_write_room, .write_room = acm_tty_write_room,
.ioctl = acm_tty_ioctl, .ioctl = acm_tty_ioctl,

View File

@ -89,7 +89,6 @@ struct acm {
struct usb_device *dev; /* the corresponding usb device */ struct usb_device *dev; /* the corresponding usb device */
struct usb_interface *control; /* control interface */ struct usb_interface *control; /* control interface */
struct usb_interface *data; /* data interface */ struct usb_interface *data; /* data interface */
struct tty_struct *tty; /* the corresponding tty */
struct tty_port port; /* our tty port data */ struct tty_port port; /* our tty port data */
struct urb *ctrlurb; /* urbs */ struct urb *ctrlurb; /* urbs */
u8 *ctrl_buffer; /* buffers of urbs */ u8 *ctrl_buffer; /* buffers of urbs */
@ -121,7 +120,6 @@ struct acm {
unsigned int ctrlout; /* output control lines (DTR, RTS) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */
unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int writesize; /* max packet size for the output bulk endpoint */
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
unsigned int used; /* someone has this acm's device open */
unsigned int minor; /* acm minor number */ unsigned int minor; /* acm minor number */
unsigned char throttle; /* throttled by tty layer */ unsigned char throttle; /* throttled by tty layer */
unsigned char clocal; /* termios CLOCAL */ unsigned char clocal; /* termios CLOCAL */