adb.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 913 行 · 第 1/2 页

C
913
字号
	va_list list;	int i, use_sreq;	int rc;	if ((adb_controller == NULL) || (adb_controller->send_request == NULL))		return -ENXIO;	if (nbytes < 1)		return -EINVAL;	if (req == NULL && (flags & ADBREQ_NOSEND))		return -EINVAL;		if (req == NULL) {		if (test_and_set_bit(0,&adb_sreq_lock)) {			printk("adb.c: Warning: contention on static request !\n");			return -EPERM;		}		req = &adb_sreq;		flags |= ADBREQ_SYNC;		use_sreq = 1;	} else		use_sreq = 0;	req->nbytes = nbytes+1;	req->done = done;	req->reply_expected = flags & ADBREQ_REPLY;	req->data[0] = ADB_PACKET;	va_start(list, nbytes);	for (i = 0; i < nbytes; ++i)		req->data[i+1] = va_arg(list, int);	va_end(list);	if (flags & ADBREQ_NOSEND)		return 0;	/* Synchronous requests send from the probe thread cause it to	 * block. Beware that the "done" callback will be overriden !	 */	if ((flags & ADBREQ_SYNC) &&	    (current->pid && adb_probe_task_pid &&	    adb_probe_task_pid == current->pid)) {		req->done = adb_probe_wakeup;		rc = adb_controller->send_request(req, 0);		if (rc || req->complete)			goto bail;		wait_for_completion(&adb_probe_task_comp);		rc = 0;		goto bail;	}	rc = adb_controller->send_request(req, flags & ADBREQ_SYNC);bail:	if (use_sreq)		clear_bit(0, &adb_sreq_lock);	return rc;} /* Ultimately this should return the number of devices with    the given default id.    And it does it now ! Note: changed behaviour: This function    will now register if default_id _and_ handler_id both match    but handler_id can be left to 0 to match with default_id only.    When handler_id is set, this function will try to adjust    the handler_id id it doesn't match. */intadb_register(int default_id, int handler_id, struct adb_ids *ids,	     void (*handler)(unsigned char *, int, struct pt_regs *, int)){	int i;	down(&adb_handler_sem);	ids->nids = 0;	for (i = 1; i < 16; i++) {		if ((adb_handler[i].original_address == default_id) &&		    (!handler_id || (handler_id == adb_handler[i].handler_id) || 		    try_handler_change(i, handler_id))) {			if (adb_handler[i].handler != 0) {				printk(KERN_ERR				       "Two handlers for ADB device %d\n",				       default_id);				continue;			}			write_lock_irq(&adb_handler_lock);			adb_handler[i].handler = handler;			write_unlock_irq(&adb_handler_lock);			ids->id[ids->nids++] = i;		}	}	up(&adb_handler_sem);	return ids->nids;}intadb_unregister(int index){	int ret = -ENODEV;	down(&adb_handler_sem);	write_lock_irq(&adb_handler_lock);	if (adb_handler[index].handler) {		while(adb_handler[index].busy) {			write_unlock_irq(&adb_handler_lock);			yield();			write_lock_irq(&adb_handler_lock);		}		ret = 0;		adb_handler[index].handler = NULL;	}	write_unlock_irq(&adb_handler_lock);	up(&adb_handler_sem);	return ret;}voidadb_input(unsigned char *buf, int nb, struct pt_regs *regs, int autopoll){	int i, id;	static int dump_adb_input = 0;	unsigned long flags;		void (*handler)(unsigned char *, int, struct pt_regs *, int);	/* We skip keystrokes and mouse moves when the sleep process	 * has been started. We stop autopoll, but this is another security	 */	if (adb_got_sleep)		return;			id = buf[0] >> 4;	if (dump_adb_input) {		printk(KERN_INFO "adb packet: ");		for (i = 0; i < nb; ++i)			printk(" %x", buf[i]);		printk(", id = %d\n", id);	}	write_lock_irqsave(&adb_handler_lock, flags);	handler = adb_handler[id].handler;	if (handler != NULL)		adb_handler[id].busy = 1;	write_unlock_irqrestore(&adb_handler_lock, flags);	if (handler != NULL) {		(*handler)(buf, nb, regs, autopoll);		wmb();		adb_handler[id].busy = 0;	}		}/* Try to change handler to new_id. Will return 1 if successful. */static int try_handler_change(int address, int new_id){	struct adb_request req;	if (adb_handler[address].handler_id == new_id)	    return 1;	adb_request(&req, NULL, ADBREQ_SYNC, 3,	    ADB_WRITEREG(address, 3), address | 0x20, new_id);	adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,	    ADB_READREG(address, 3));	if (req.reply_len < 2)	    return 0;	if (req.reply[2] != new_id)	    return 0;	adb_handler[address].handler_id = req.reply[2];	return 1;}intadb_try_handler_change(int address, int new_id){	int ret;	down(&adb_handler_sem);	ret = try_handler_change(address, new_id);	up(&adb_handler_sem);	return ret;}intadb_get_infos(int address, int *original_address, int *handler_id){	down(&adb_handler_sem);	*original_address = adb_handler[address].original_address;	*handler_id = adb_handler[address].handler_id;	up(&adb_handler_sem);	return (*original_address != 0);}/* * /dev/adb device driver. */#define ADB_MAJOR	56	/* major number for /dev/adb */struct adbdev_state {	spinlock_t	lock;	atomic_t	n_pending;	struct adb_request *completed;  	wait_queue_head_t wait_queue;	int		inuse;};static void adb_write_done(struct adb_request *req){	struct adbdev_state *state = (struct adbdev_state *) req->arg;	unsigned long flags;	if (!req->complete) {		req->reply_len = 0;		req->complete = 1;	}	spin_lock_irqsave(&state->lock, flags);	atomic_dec(&state->n_pending);	if (!state->inuse) {		kfree(req);		if (atomic_read(&state->n_pending) == 0) {			spin_unlock_irqrestore(&state->lock, flags);			kfree(state);			return;		}	} else {		struct adb_request **ap = &state->completed;		while (*ap != NULL)			ap = &(*ap)->next;		req->next = NULL;		*ap = req;		wake_up_interruptible(&state->wait_queue);	}	spin_unlock_irqrestore(&state->lock, flags);}static intdo_adb_query(struct adb_request *req){	int	ret = -EINVAL;	switch(req->data[1])	{	case ADB_QUERY_GETDEVINFO:		if (req->nbytes < 3)			break;		down(&adb_handler_sem);		req->reply[0] = adb_handler[req->data[2]].original_address;		req->reply[1] = adb_handler[req->data[2]].handler_id;		up(&adb_handler_sem);		req->complete = 1;		req->reply_len = 2;		adb_write_done(req);		ret = 0;		break;	}	return ret;}static int adb_open(struct inode *inode, struct file *file){	struct adbdev_state *state;	if (iminor(inode) > 0 || adb_controller == NULL)		return -ENXIO;	state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);	if (state == 0)		return -ENOMEM;	file->private_data = state;	spin_lock_init(&state->lock);	atomic_set(&state->n_pending, 0);	state->completed = NULL;	init_waitqueue_head(&state->wait_queue);	state->inuse = 1;	return 0;}static int adb_release(struct inode *inode, struct file *file){	struct adbdev_state *state = file->private_data;	unsigned long flags;	lock_kernel();	if (state) {		file->private_data = NULL;		spin_lock_irqsave(&state->lock, flags);		if (atomic_read(&state->n_pending) == 0		    && state->completed == NULL) {			spin_unlock_irqrestore(&state->lock, flags);			kfree(state);		} else {			state->inuse = 0;			spin_unlock_irqrestore(&state->lock, flags);		}	}	unlock_kernel();	return 0;}static ssize_t adb_read(struct file *file, char __user *buf,			size_t count, loff_t *ppos){	int ret;	struct adbdev_state *state = file->private_data;	struct adb_request *req;	wait_queue_t wait = __WAITQUEUE_INITIALIZER(wait,current);	unsigned long flags;	if (count < 2)		return -EINVAL;	if (count > sizeof(req->reply))		count = sizeof(req->reply);	ret = verify_area(VERIFY_WRITE, buf, count);	if (ret)		return ret;	req = NULL;	spin_lock_irqsave(&state->lock, flags);	add_wait_queue(&state->wait_queue, &wait);	current->state = TASK_INTERRUPTIBLE;	for (;;) {		req = state->completed;		if (req != NULL)			state->completed = req->next;		else if (atomic_read(&state->n_pending) == 0)			ret = -EIO;		if (req != NULL || ret != 0)			break;				if (file->f_flags & O_NONBLOCK) {			ret = -EAGAIN;			break;		}		if (signal_pending(current)) {			ret = -ERESTARTSYS;			break;		}		spin_unlock_irqrestore(&state->lock, flags);		schedule();		spin_lock_irqsave(&state->lock, flags);	}	current->state = TASK_RUNNING;	remove_wait_queue(&state->wait_queue, &wait);	spin_unlock_irqrestore(&state->lock, flags);		if (ret)		return ret;	ret = req->reply_len;	if (ret > count)		ret = count;	if (ret > 0 && copy_to_user(buf, req->reply, ret))		ret = -EFAULT;	kfree(req);	return ret;}static ssize_t adb_write(struct file *file, const char __user *buf,			 size_t count, loff_t *ppos){	int ret/*, i*/;	struct adbdev_state *state = file->private_data;	struct adb_request *req;	if (count < 2 || count > sizeof(req->data))		return -EINVAL;	if (adb_controller == NULL)		return -ENXIO;	ret = verify_area(VERIFY_READ, buf, count);	if (ret)		return ret;	req = (struct adb_request *) kmalloc(sizeof(struct adb_request),					     GFP_KERNEL);	if (req == NULL)		return -ENOMEM;	req->nbytes = count;	req->done = adb_write_done;	req->arg = (void *) state;	req->complete = 0;		ret = -EFAULT;	if (copy_from_user(req->data, buf, count))		goto out;	atomic_inc(&state->n_pending);	/* If a probe is in progress or we are sleeping, wait for it to complete */	down(&adb_probe_mutex);	/* Queries are special requests sent to the ADB driver itself */	if (req->data[0] == ADB_QUERY) {		if (count > 1)			ret = do_adb_query(req);		else			ret = -EINVAL;		up(&adb_probe_mutex);	}	/* Special case for ADB_BUSRESET request, all others are sent to	   the controller */	else if ((req->data[0] == ADB_PACKET)&&(count > 1)		&&(req->data[1] == ADB_BUSRESET)) {		ret = do_adb_reset_bus();		up(&adb_probe_mutex);		atomic_dec(&state->n_pending);		if (ret == 0)			ret = count;		goto out;	} else {			req->reply_expected = ((req->data[1] & 0xc) == 0xc);		if (adb_controller && adb_controller->send_request)			ret = adb_controller->send_request(req, 0);		else			ret = -ENXIO;		up(&adb_probe_mutex);	}	if (ret != 0) {		atomic_dec(&state->n_pending);		goto out;	}	return count;out:	kfree(req);	return ret;}static struct file_operations adb_fops = {	.owner		= THIS_MODULE,	.llseek		= no_llseek,	.read		= adb_read,	.write		= adb_write,	.open		= adb_open,	.release	= adb_release,};static voidadbdev_init(void){	if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) {		printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);		return;	}	devfs_mk_cdev(MKDEV(ADB_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR, "adb");	adb_dev_class = class_simple_create(THIS_MODULE, "adb");	if (IS_ERR(adb_dev_class)) {		return;	}	class_simple_device_add(adb_dev_class, MKDEV(ADB_MAJOR, 0), NULL, "adb");}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?