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 + -
显示快捷键?