hvcs.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,667 行 · 第 1/4 页
C
1,667 行
* If there wasn't any pi when the device was added it doesn't meant * there isn't any now. This driver isn't notified when a new partner * vty is added to a vty-server so we discover changes on our own. * Please see comments in hvcs_register_connection() for justification * of this bizarre code. */ retval = hvcs_register_connection(unit_address, hvcsd->p_partition_ID, hvcsd->p_unit_address); if (!retval) { hvcsd->connected = 1; return 0; } else if (retval != -EINVAL) return retval; /* * As per the spec re-get the pi and try again if -EINVAL after the * first connection attempt. */ if (hvcs_get_pi(hvcsd)) return -ENOMEM; if (!hvcs_has_pi(hvcsd)) return -ENODEV; retval = hvcs_register_connection(unit_address, hvcsd->p_partition_ID, hvcsd->p_unit_address); if (retval != -EINVAL) { hvcsd->connected = 1; return retval; } /* * EBUSY is the most likely scenario though the vty could have been * removed or there really could be an hcall error due to the parameter * data but thanks to ambiguous firmware return codes we can't really * tell. */ printk(KERN_INFO "HVCS: vty-server or partner" " vty is busy. Try again later.\n"); return -EBUSY;}/* This function must be called with the hvcsd->lock held */static void hvcs_partner_free(struct hvcs_struct *hvcsd){ int retval; do { retval = hvcs_free_connection(hvcsd->vdev->unit_address); } while (retval == -EBUSY); hvcsd->connected = 0;}/* This helper function must be called WITHOUT the hvcsd->lock held */static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, unsigned int irq, struct vio_dev *vdev){ unsigned long flags; int rc; /* * It is possible that the vty-server was removed between the time that * the conn was registered and now. */ if (!(rc = request_irq(irq, &hvcs_handle_interrupt, SA_INTERRUPT, "ibmhvcs", hvcsd))) { /* * It is possible the vty-server was removed after the irq was * requested but before we have time to enable interrupts. */ if (vio_enable_interrupts(vdev) == H_Success) return 0; else { printk(KERN_ERR "HVCS: int enable failed for" " vty-server@%X.\n", unit_address); free_irq(irq, hvcsd); } } else printk(KERN_ERR "HVCS: irq req failed for" " vty-server@%X.\n", unit_address); spin_lock_irqsave(&hvcsd->lock, flags); hvcs_partner_free(hvcsd); spin_unlock_irqrestore(&hvcsd->lock, flags); return rc;}/* * This always increments the kobject ref count if the call is successful. * Please remember to dec when you are done with the instance. * * NOTICE: Do NOT hold either the hvcs_struct.lock or hvcs_structs_lock when * calling this function or you will get deadlock. */struct hvcs_struct *hvcs_get_by_index(int index){ struct hvcs_struct *hvcsd = NULL; unsigned long flags; spin_lock(&hvcs_structs_lock); /* We can immediately discard OOB requests */ if (index >= 0 && index < HVCS_MAX_SERVER_ADAPTERS) { list_for_each_entry(hvcsd, &hvcs_structs, next) { spin_lock_irqsave(&hvcsd->lock, flags); if (hvcsd->index == index) { kobject_get(&hvcsd->kobj); spin_unlock_irqrestore(&hvcsd->lock, flags); spin_unlock(&hvcs_structs_lock); return hvcsd; } spin_unlock_irqrestore(&hvcsd->lock, flags); } hvcsd = NULL; } spin_unlock(&hvcs_structs_lock); return hvcsd;}/* * This is invoked via the tty_open interface when a user app connects to the * /dev node. */static int hvcs_open(struct tty_struct *tty, struct file *filp){ struct hvcs_struct *hvcsd; int rc, retval = 0; unsigned long flags; unsigned int irq; struct vio_dev *vdev; unsigned long unit_address; struct kobject *kobjp; if (tty->driver_data) goto fast_open; /* * Is there a vty-server that shares the same index? * This function increments the kobject index. */ if (!(hvcsd = hvcs_get_by_index(tty->index))) { printk(KERN_WARNING "HVCS: open failed, no device associated" " with tty->index %d.\n", tty->index); return -ENODEV; } spin_lock_irqsave(&hvcsd->lock, flags); if (hvcsd->connected == 0) if ((retval = hvcs_partner_connect(hvcsd))) goto error_release; hvcsd->open_count = 1; hvcsd->tty = tty; tty->driver_data = hvcsd; /* * Set this driver to low latency so that we actually have a chance at * catching a throttled TTY after we flip_buffer_push. Otherwise the * flush_to_async may not execute until after the kernel_thread has * yielded and resumed the next flip_buffer_push resulting in data * loss. */ tty->low_latency = 1; memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); /* * Save these in the spinlock for the enable operations that need them * outside of the spinlock. */ irq = hvcsd->vdev->irq; vdev = hvcsd->vdev; unit_address = hvcsd->vdev->unit_address; hvcsd->todo_mask |= HVCS_SCHED_READ; spin_unlock_irqrestore(&hvcsd->lock, flags); /* * This must be done outside of the spinlock because it requests irqs * and will grab the spinlock and free the connection if it fails. */ if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) { kobject_put(&hvcsd->kobj); printk(KERN_WARNING "HVCS: enable device failed.\n"); return rc; } goto open_success;fast_open: hvcsd = tty->driver_data; spin_lock_irqsave(&hvcsd->lock, flags); if (!kobject_get(&hvcsd->kobj)) { spin_unlock_irqrestore(&hvcsd->lock, flags); printk(KERN_ERR "HVCS: Kobject of open" " hvcs doesn't exist.\n"); return -EFAULT; /* Is this the right return value? */ } hvcsd->open_count++; hvcsd->todo_mask |= HVCS_SCHED_READ; spin_unlock_irqrestore(&hvcsd->lock, flags);open_success: hvcs_kick(); printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n", hvcsd->vdev->unit_address ); return 0;error_release: kobjp = &hvcsd->kobj; spin_unlock_irqrestore(&hvcsd->lock, flags); kobject_put(&hvcsd->kobj); printk(KERN_WARNING "HVCS: partner connect failed.\n"); return retval;}static void hvcs_close(struct tty_struct *tty, struct file *filp){ struct hvcs_struct *hvcsd; unsigned long flags; struct kobject *kobjp; int irq = NO_IRQ; /* * Is someone trying to close the file associated with this device after * we have hung up? If so tty->driver_data wouldn't be valid. */ if (tty_hung_up_p(filp)) return; /* * No driver_data means that this close was probably issued after a * failed hvcs_open by the tty layer's release_dev() api and we can just * exit cleanly. */ if (!tty->driver_data) return; hvcsd = tty->driver_data; spin_lock_irqsave(&hvcsd->lock, flags); kobjp = &hvcsd->kobj; if (--hvcsd->open_count == 0) { vio_disable_interrupts(hvcsd->vdev); /* * NULL this early so that the kernel_thread doesn't try to * execute any operations on the TTY even though it is obligated * to deliver any pending I/O to the hypervisor. */ hvcsd->tty = NULL; irq = hvcsd->vdev->irq; spin_unlock_irqrestore(&hvcsd->lock, flags); tty_wait_until_sent(tty, HVCS_CLOSE_WAIT); /* * This line is important because it tells hvcs_open that this * device needs to be re-configured the next time hvcs_open is * called. */ tty->driver_data = NULL; free_irq(irq, hvcsd); kobject_put(kobjp); return; } else if (hvcsd->open_count < 0) { printk(KERN_ERR "HVCS: vty-server@%X open_count: %d" " is missmanaged.\n", hvcsd->vdev->unit_address, hvcsd->open_count); } spin_unlock_irqrestore(&hvcsd->lock, flags); kobject_put(kobjp);}static void hvcs_hangup(struct tty_struct * tty){ struct hvcs_struct *hvcsd = tty->driver_data; unsigned long flags; int temp_open_count; struct kobject *kobjp; int irq = NO_IRQ; spin_lock_irqsave(&hvcsd->lock, flags); /* Preserve this so that we know how many kobject refs to put */ temp_open_count = hvcsd->open_count; /* * Don't kobject put inside the spinlock because the destruction * callback may use the spinlock and it may get called before the * spinlock has been released. Get a pointer to the kobject and * kobject_put on that after releasing the spinlock. */ kobjp = &hvcsd->kobj; vio_disable_interrupts(hvcsd->vdev); hvcsd->todo_mask = 0; /* I don't think the tty needs the hvcs_struct pointer after a hangup */ hvcsd->tty->driver_data = NULL; hvcsd->tty = NULL; hvcsd->open_count = 0; /* This will drop any buffered data on the floor which is OK in a hangup * scenario. */ memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); hvcsd->chars_in_buffer = 0; irq = hvcsd->vdev->irq; spin_unlock_irqrestore(&hvcsd->lock, flags); free_irq(irq, hvcsd); /* * We need to kobject_put() for every open_count we have since the * tty_hangup() function doesn't invoke a close per open connection on a * non-console device. */ while(temp_open_count) { --temp_open_count; /* * The final put will trigger destruction of the hvcs_struct. * NOTE: If this hangup was signaled from user space then the * final put will never happen. */ kobject_put(kobjp); }}/* * NOTE: This is almost always from_user since user level apps interact with the * /dev nodes. I'm trusting that if hvcs_write gets called and interrupted by * hvcs_remove (which removes the target device and executes tty_hangup()) that * tty_hangup will allow hvcs_write time to complete execution before it * terminates our device. */static int hvcs_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count){ struct hvcs_struct *hvcsd = tty->driver_data; unsigned int unit_address; unsigned char *charbuf; unsigned long flags; int total_sent = 0; int tosend = 0; int result = 0; /* * If they don't check the return code off of their open they may * attempt this even if there is no connected device. */ if (!hvcsd) return -ENODEV; /* Reasonable size to prevent user level flooding */ if (count > HVCS_MAX_FROM_USER) { printk(KERN_WARNING "HVCS write: count being truncated to" " HVCS_MAX_FROM_USER.\n"); count = HVCS_MAX_FROM_USER; } if (!from_user) charbuf = (unsigned char *)buf; else { charbuf = kmalloc(count, GFP_KERNEL); if (!charbuf) { printk(KERN_WARNING "HVCS: write -ENOMEM.\n"); return -ENOMEM; } if (copy_from_user(charbuf, buf, count)) { kfree(charbuf); printk(KERN_WARNING "HVCS: write -EFAULT.\n"); return -EFAULT; } } spin_lock_irqsave(&hvcsd->lock, flags); /* * Somehow an open succedded but the device was removed or the * connection terminated between the vty-server and partner vty during * the middle of a write operation? This is a crummy place to do this * but we want to keep it all in the spinlock. */ if (hvcsd->open_count <= 0) { spin_unlock_irqrestore(&hvcsd->lock, flags); if (from_user) kfree(charbuf); return -ENODEV; } unit_address = hvcsd->vdev->unit_address; while (count > 0) { tosend = min(count, (HVCS_BUFF_LEN - hvcsd->chars_in_buffer)); /* * No more space, this probably means that the last call to * hvcs_write() didn't succeed and the buffer was filled up. */ if (!tosend) break;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?