hvcs.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,667 行 · 第 1/4 页
C
1,667 行
hvcsd->todo_mask &= ~(HVCS_TRY_WRITE); /* wmb(); */ /* * We are still obligated to deliver the data to the * hypervisor even if the tty has been closed because * we commited to delivering it. But don't try to wake * a non-existent tty. */ if (tty) { tty_wakeup(tty); } } }}static int hvcs_io(struct hvcs_struct *hvcsd){ uint32_t unit_address; struct tty_struct *tty; char buf[HVCS_BUFF_LEN] __ALIGNED__; unsigned long flags; int got = 0; int i; spin_lock_irqsave(&hvcsd->lock, flags); unit_address = hvcsd->vdev->unit_address; tty = hvcsd->tty; hvcs_try_write(hvcsd); if (!tty || test_bit(TTY_THROTTLED, &tty->flags)) { hvcsd->todo_mask &= ~(HVCS_READ_MASK); goto bail; } else if (!(hvcsd->todo_mask & (HVCS_READ_MASK))) goto bail; /* remove the read masks */ hvcsd->todo_mask &= ~(HVCS_READ_MASK); if ((tty->flip.count + HVCS_BUFF_LEN) < TTY_FLIPBUF_SIZE) { got = hvc_get_chars(unit_address, &buf[0], HVCS_BUFF_LEN); for (i=0;got && i<got;i++) tty_insert_flip_char(tty, buf[i], TTY_NORMAL); } /* Give the TTY time to process the data we just sent. */ if (got) hvcsd->todo_mask |= HVCS_QUICK_READ; spin_unlock_irqrestore(&hvcsd->lock, flags); if (tty->flip.count) { /* This is synch because tty->low_latency == 1 */ tty_flip_buffer_push(tty); } if (!got) { /* Do this _after_ the flip_buffer_push */ spin_lock_irqsave(&hvcsd->lock, flags); vio_enable_interrupts(hvcsd->vdev); spin_unlock_irqrestore(&hvcsd->lock, flags); } return hvcsd->todo_mask; bail: spin_unlock_irqrestore(&hvcsd->lock, flags); return hvcsd->todo_mask;}static int khvcsd(void *unused){ struct hvcs_struct *hvcsd; int hvcs_todo_mask; __set_current_state(TASK_RUNNING); do { hvcs_todo_mask = 0; hvcs_kicked = 0; wmb(); spin_lock(&hvcs_structs_lock); list_for_each_entry(hvcsd, &hvcs_structs, next) { hvcs_todo_mask |= hvcs_io(hvcsd); } spin_unlock(&hvcs_structs_lock); /* * If any of the hvcs adapters want to try a write or quick read * don't schedule(), yield a smidgen then execute the hvcs_io * thread again for those that want the write. */ if (hvcs_todo_mask & (HVCS_TRY_WRITE | HVCS_QUICK_READ)) { yield(); continue; } set_current_state(TASK_INTERRUPTIBLE); if (!hvcs_kicked) schedule(); __set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); return 0;}static struct vio_device_id hvcs_driver_table[] __devinitdata= { {"serial-server", "hvterm2"}, { 0, }};MODULE_DEVICE_TABLE(vio, hvcs_driver_table);static void hvcs_return_index(int index){ /* Paranoia check */ if (!hvcs_index_list) return; if (index < 0 || index >= hvcs_index_count) return; if (hvcs_index_list[index] == -1) return; else hvcs_index_list[index] = -1;}/* callback when the kboject ref count reaches zero */static void destroy_hvcs_struct(struct kobject *kobj){ struct hvcs_struct *hvcsd = from_kobj(kobj); struct vio_dev *vdev; unsigned long flags; spin_lock(&hvcs_structs_lock); spin_lock_irqsave(&hvcsd->lock, flags); /* the list_del poisons the pointers */ list_del(&(hvcsd->next)); if (hvcsd->connected == 1) { hvcs_partner_free(hvcsd); printk(KERN_INFO "HVCS: Closed vty-server@%X and" " partner vty@%X:%d connection.\n", hvcsd->vdev->unit_address, hvcsd->p_unit_address, (uint32_t)hvcsd->p_partition_ID); } printk(KERN_INFO "HVCS: Destroyed hvcs_struct for vty-server@%X.\n", hvcsd->vdev->unit_address); vdev = hvcsd->vdev; hvcsd->vdev = NULL; hvcsd->p_unit_address = 0; hvcsd->p_partition_ID = 0; hvcs_return_index(hvcsd->index); memset(&hvcsd->p_location_code[0], 0x00, HVCS_CLC_LENGTH + 1); spin_unlock_irqrestore(&hvcsd->lock, flags); spin_unlock(&hvcs_structs_lock); hvcs_remove_device_attrs(vdev); kfree(hvcsd);}static struct kobj_type hvcs_kobj_type = { .release = destroy_hvcs_struct,};static int hvcs_get_index(void){ int i; /* Paranoia check */ if (!hvcs_index_list) { printk(KERN_ERR "HVCS: hvcs_index_list NOT valid!.\n"); return -EFAULT; } /* Find the numerically lowest first free index. */ for(i = 0; i < hvcs_index_count; i++) { if (hvcs_index_list[i] == -1) { hvcs_index_list[i] = 0; return i; } } return -1;}static int __devinit hvcs_probe( struct vio_dev *dev, const struct vio_device_id *id){ struct hvcs_struct *hvcsd; int index; if (!dev || !id) { printk(KERN_ERR "HVCS: probed with invalid parameter.\n"); return -EPERM; } /* early to avoid cleanup on failure */ index = hvcs_get_index(); if (index < 0) { return -EFAULT; } hvcsd = kmalloc(sizeof(*hvcsd), GFP_KERNEL); if (!hvcsd) return -ENODEV; /* hvcsd->tty is zeroed out with the memset */ memset(hvcsd, 0x00, sizeof(*hvcsd)); hvcsd->lock = SPIN_LOCK_UNLOCKED; /* Automatically incs the refcount the first time */ kobject_init(&hvcsd->kobj); /* Set up the callback for terminating the hvcs_struct's life */ hvcsd->kobj.ktype = &hvcs_kobj_type; hvcsd->vdev = dev; dev->dev.driver_data = hvcsd; hvcsd->index = index; /* hvcsd->index = ++hvcs_struct_count; */ hvcsd->chars_in_buffer = 0; hvcsd->todo_mask = 0; hvcsd->connected = 0; /* * This will populate the hvcs_struct's partner info fields for the * first time. */ if (hvcs_get_pi(hvcsd)) { printk(KERN_ERR "HVCS: Failed to fetch partner" " info for vty-server@%X on device probe.\n", hvcsd->vdev->unit_address); } /* * If a user app opens a tty that corresponds to this vty-server before * the hvcs_struct has been added to the devices list then the user app * will get -ENODEV. */ spin_lock(&hvcs_structs_lock); list_add_tail(&(hvcsd->next), &hvcs_structs); spin_unlock(&hvcs_structs_lock); hvcs_create_device_attrs(hvcsd); printk(KERN_INFO "HVCS: vty-server@%X added to the vio bus.\n", dev->unit_address); /* * DON'T enable interrupts here because there is no user to receive the * data. */ return 0;}static int __devexit hvcs_remove(struct vio_dev *dev){ struct hvcs_struct *hvcsd = dev->dev.driver_data; unsigned long flags; struct kobject *kobjp; struct tty_struct *tty; if (!hvcsd) return -ENODEV; /* By this time the vty-server won't be getting any more interrups */ spin_lock_irqsave(&hvcsd->lock, flags); tty = hvcsd->tty; kobjp = &hvcsd->kobj; spin_unlock_irqrestore(&hvcsd->lock, flags); /* * Let the last holder of this object cause it to be removed, which * would probably be tty_hangup below. */ kobject_put (kobjp); /* * The hangup is a scheduled function which will auto chain call * hvcs_hangup. The tty should always be valid at this time unless a * simultaneous tty close already cleaned up the hvcs_struct. */ if (tty) tty_hangup(tty); printk(KERN_INFO "HVCS: vty-server@%X removed from the" " vio bus.\n", dev->unit_address); return 0;};static struct vio_driver hvcs_vio_driver = { .name = hvcs_driver_name, .id_table = hvcs_driver_table, .probe = hvcs_probe, .remove = hvcs_remove,};/* Only called from hvcs_get_pi please */static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd){ int clclength; hvcsd->p_unit_address = pi->unit_address; hvcsd->p_partition_ID = pi->partition_ID; clclength = strlen(&pi->location_code[0]); if (clclength > HVCS_CLC_LENGTH) clclength = HVCS_CLC_LENGTH; /* copy the null-term char too */ strncpy(&hvcsd->p_location_code[0], &pi->location_code[0], clclength + 1);}/* * Traverse the list and add the partner info that is found to the hvcs_struct * struct entry. NOTE: At this time I know that partner info will return a * single entry but in the future there may be multiple partner info entries per * vty-server and you'll want to zero out that list and reset it. If for some * reason you have an old version of this driver but there IS more than one * partner info then hvcsd->p_* will hold the last partner info data from the * firmware query. A good way to update this code would be to replace the three * partner info fields in hvcs_struct with a list of hvcs_partner_info * instances. * * This function must be called with the hvcsd->lock held. */static int hvcs_get_pi(struct hvcs_struct *hvcsd){ struct hvcs_partner_info *pi; uint32_t unit_address = hvcsd->vdev->unit_address; struct list_head head; int retval; spin_lock(&hvcs_pi_lock); if (!hvcs_pi_buff) { spin_unlock(&hvcs_pi_lock); return -EFAULT; } retval = hvcs_get_partner_info(unit_address, &head, hvcs_pi_buff); spin_unlock(&hvcs_pi_lock); if (retval) { printk(KERN_ERR "HVCS: Failed to fetch partner" " info for vty-server@%x.\n", unit_address); return retval; } /* nixes the values if the partner vty went away */ hvcsd->p_unit_address = 0; hvcsd->p_partition_ID = 0; list_for_each_entry(pi, &head, node) hvcs_set_pi(pi, hvcsd); hvcs_free_partner_info(&head); return 0;}/* * This function is executed by the driver "rescan" sysfs entry. It shouldn't * be executed elsewhere, in order to prevent deadlock issues. */static int hvcs_rescan_devices_list(void){ struct hvcs_struct *hvcsd; unsigned long flags; spin_lock(&hvcs_structs_lock); list_for_each_entry(hvcsd, &hvcs_structs, next) { spin_lock_irqsave(&hvcsd->lock, flags); hvcs_get_pi(hvcsd); spin_unlock_irqrestore(&hvcsd->lock, flags); } spin_unlock(&hvcs_structs_lock); return 0;}/* * Farm this off into its own function because it could be more complex once * multiple partners support is added. This function should be called with * the hvcsd->lock held. */static int hvcs_has_pi(struct hvcs_struct *hvcsd){ if ((!hvcsd->p_unit_address) || (!hvcsd->p_partition_ID)) return 0; return 1;}/* * NOTE: It is possible that the super admin removed a partner vty and then * added a different vty as the new partner. * * This function must be called with the hvcsd->lock held. */static int hvcs_partner_connect(struct hvcs_struct *hvcsd){ int retval; unsigned int unit_address = hvcsd->vdev->unit_address; /*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?