Re: [PATCH] usb: The HID Simple Driver Interface 0.3.0

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



liyu wrote:
> You can see one example in  "[PATCH] usb: Microsoft Natural Ergonomic Keyboard 4000 Driver 0.3.0"
> 
> Changelogs: ( upto 20060803)
> 
> 	1. Release version 0.3.0.	
> 
> Changelogs: ( upto 20060728)
> 
> 	1. Fixed the inline function redefine error.
> 	2. Fixed the bug that access invalid address hidinput_simple_driver_pop().
> 	
> Signed-off-by: Liyu <[email protected]>
> 
> diff -Naurp linux-2.6.17.7/drivers/usb/input.orig/hid-core.c linux-2.6.17.7/drivers/usb/input/hid-core.c
> --- linux-2.6.17.7/drivers/usb/input.orig/hid-core.c	2006-07-25 11:36:01.000000000 +0800
> +++ linux-2.6.17.7/drivers/usb/input/hid-core.c	2006-08-03 15:44:45.000000000 +0800
> @@ -4,6 +4,7 @@
>   *  Copyright (c) 1999 Andreas Gal
>   *  Copyright (c) 2000-2005 Vojtech Pavlik <[email protected]>
>   *  Copyright (c) 2005 Michael Haboustak <[email protected]> for Concept2, Inc
> + *  Copyright (c) 2006 Liyu <[email protected]>  HID simple driver interface
>   */
>  
>  /*
> @@ -26,6 +27,7 @@
>  #include <asm/byteorder.h>
>  #include <linux/input.h>
>  #include <linux/wait.h>
> +#include <asm/semaphore.h>
>  
>  #undef DEBUG
>  #undef DEBUG_DATA
> @@ -33,6 +35,7 @@
>  #include <linux/usb.h>
>  
>  #include "hid.h"
> +#include "hid-simple.h"
>  #include <linux/hiddev.h>
>  
>  /*
> @@ -46,6 +49,15 @@
>  
>  static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
>  				"Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
> +
> +/*
> + * The global data structure for simple device driver interface.
> + */
> +static DECLARE_MUTEX(matched_lock);
> +static DECLARE_MUTEX(simple_lock);
> +static struct list_head matched_devices_list;
> +static struct list_head simple_drivers_list;
> +
>  /*
>   * Module parameters.
>   */
> @@ -785,8 +797,17 @@ static __inline__ int search(__s32 *arra
>  static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt, struct pt_regs *regs)
>  {
>  	hid_dump_input(usage, value);
> -	if (hid->claimed & HID_CLAIMED_INPUT)
> +	if (hid->claimed & HID_CLAIMED_INPUT) {
> +		if (hid->simple) {
> +			if (hid->simple->pre_event &&
> +				!hid->simple->pre_event(hid, field, usage, 
> +								value, regs))
> +			return;
> +		}
>  		hidinput_hid_event(hid, field, usage, value, regs);
> +		if (hid->simple && hid->simple->post_event)
> +			hid->simple->post_event(hid, field, usage, value, regs);
> +	}
>  	if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt)
>  		hiddev_hid_event(hid, field, usage, value, regs);
>  }
> @@ -832,7 +853,6 @@ static void hid_input_field(struct hid_d
>  			&& field->usage[field->value[n] - min].hid
>  			&& search(value, field->value[n], count))
>  				hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt, regs);
> -
>  		if (value[n] >= min && value[n] <= max
>  			&& field->usage[value[n] - min].hid
>  			&& search(field->value, value[n], count))
> @@ -1977,6 +1997,8 @@ fail:
>  static void hid_disconnect(struct usb_interface *intf)
>  {
>  	struct hid_device *hid = usb_get_intfdata (intf);
> +	struct list_head *node;
> +	struct matched_device *matched;
>  
>  	if (!hid)
>  		return;
> @@ -1991,8 +2013,29 @@ static void hid_disconnect(struct usb_in
>  	del_timer_sync(&hid->io_retry);
>  	flush_scheduled_work();
>  
> -	if (hid->claimed & HID_CLAIMED_INPUT)
> +	if (hid->claimed & HID_CLAIMED_INPUT) {
> +		matched = NULL;
> +		down(&matched_lock);
> +		list_for_each(node, &matched_devices_list) {
> +			matched = list_entry(node, struct matched_device, node);
> +			if (matched->intf == intf) {
> +				list_del(&matched->node);
> +				break;
> +			}
> +			matched = NULL;
> +		}
> +		up(&matched_lock);
> +		/* disconnect simple device driver if need */
> +		if (matched && hid->simple) {
> +			hidinput_simple_driver_disconnect(hid);
> +			hidinput_simple_driver_pop(hid, matched);
> +		}
> +		if (matched) {
> +			matched->intf = NULL;
> +			kfree(matched);
> +		}
>  		hidinput_disconnect(hid);
> +	}
>  	if (hid->claimed & HID_CLAIMED_HIDDEV)
>  		hiddev_disconnect(hid);
>  
> @@ -2005,12 +2048,143 @@ static void hid_disconnect(struct usb_in
>  	hid_free_device(hid);
>  }
>  
> +
> +/* called when we unregister a hidinput simple device driver */
> +static void
> +hidinput_simple_driver_bind_foreach(void)
> +{
> +	struct hidinput_simple_driver *simple;
> +	struct matched_device *matched=NULL;
> +	struct list_head *matched_node = NULL;
> +	struct list_head *simple_node = NULL;
> +	struct hid_device *hid=NULL;
> +
> +	down(&matched_lock);
> +	list_for_each(matched_node, &matched_devices_list) {
> +		matched = list_entry(matched_node, struct matched_device, node);
> +		hid = usb_get_intfdata(matched->intf);
> +		if (hid->simple)
> +			continue;
> +		down(&simple_lock);
> +		list_for_each(simple_node, &simple_drivers_list) {
> +			simple = list_entry(simple_node, struct hidinput_simple_driver, node);
> +			if (!usb_match_id(matched->intf, simple->id_table))
> +				continue;
> +			if (hidinput_simple_driver_connect(simple, hid))
> +				continue;
> +			if (hidinput_simple_driver_push(hid, simple, matched))
> +				continue;
> +			hidinput_simple_driver_setup_usage(hid);
> +			printk(KERN_INFO"The simple HID driver \'%s\' attach on"
> +								"\'%s\'\n",
> +								simple->name,
> +								hid->name);
> +		}
> +		up(&simple_lock);
> +	}
> +	up(&matched_lock);
> +	
> +}
> +
> +/* called in hid_probe() */
> +static void
> +hidinput_simple_driver_bind(struct matched_device *matched)
> +{
> +	struct hidinput_simple_driver *simple;
> +	struct list_head *node;
> +	struct hid_device *hid;
> +	
> +	if (!matched->intf)
> +		return;
> +	hid = usb_get_intfdata (matched->intf);
> +	if (hid->simple)
> +		return;
> +
> +	simple = NULL;
> +	down(&simple_lock);
> +	list_for_each(node, &simple_drivers_list) {
> +		simple = list_entry(node, struct hidinput_simple_driver, node);
> +		if (usb_match_id(matched->intf, simple->id_table))
> +			break;
> +		simple = NULL;
> +	}
> +	up(&simple_lock);
> +	
> +	if (!simple) 
> +		return;
> +	if (hidinput_simple_driver_connect(simple, hid))
> +		return;
> +	if (hidinput_simple_driver_push(hid, simple, matched))
> +		return;
> +	hidinput_simple_driver_setup_usage(hid);
> +
> +	printk(KERN_INFO"The simple HID driver \'%s\' attach on \'%s\'\n", 
> +						simple->name, hid->name);
> +}
> +
> +int
> +hidinput_register_simple_driver(struct hidinput_simple_driver *simple)
> +{
> +	struct list_head *node=NULL;
> +	struct matched_device *matched;
> +	struct hid_device *hid=NULL;
> +	int ret=0;
> +
> +	if (!simple || !simple->name)
> +		return -EINVAL;
> +
> +	hidinput_simple_driver_init(simple);
> +
> +	down(&matched_lock);
> +	list_for_each(node, &matched_devices_list) {
> +		matched = list_entry(node, struct matched_device, node);
> +		hid = usb_get_intfdata (matched->intf);
> +		if (hid->simple)
> +			continue;
> +		if (!usb_match_id(matched->intf, simple->id_table))
> +			continue;
> +		if (hidinput_simple_driver_connect(simple, hid))
> +			continue;
> +		if (hidinput_simple_driver_push(hid, simple, matched))
> +			continue;
> +		hidinput_simple_driver_setup_usage(hid);
> +		ret = 0;
> +		printk(KERN_INFO"The simple HID driver \'%s\' attach on"
> +							" \'%s\'.\n",
> +						 simple->name, hid->name);
> +	}
> +	up(&matched_lock);
> +
> +	down(&simple_lock);
> +	list_add(&simple->node, &simple_drivers_list);
> +	up(&simple_lock);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(hidinput_register_simple_driver);
> +
> +void
> +hidinput_unregister_simple_driver(struct hidinput_simple_driver *simple)
> +{
> +	hidinput_simple_driver_clear(simple);
> +	down(&simple_lock);
> +	list_del(&simple->node);
> +	up(&simple_lock);
> +	/* to active simple device driver that it is waiting */
> +	hidinput_simple_driver_bind_foreach();
> +	printk(KERN_INFO"The simple HID driver \'%s\' unregistered.\n", 
> +							simple->name);
> +}
> +
> +EXPORT_SYMBOL_GPL(hidinput_unregister_simple_driver);
> +
>  static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
>  {
>  	struct hid_device *hid;
>  	char path[64];
>  	int i;
>  	char *c;
> +	struct matched_device *matched;
>  
>  	dbg("HID probe called for ifnum %d",
>  			intf->altsetting->desc.bInterfaceNumber);
> @@ -2021,13 +2195,22 @@ static int hid_probe(struct usb_interfac
>  	hid_init_reports(hid);
>  	hid_dump_device(hid);
>  
> -	if (!hidinput_connect(hid))
> +	usb_set_intfdata(intf, hid);
> +
> +	if (!hidinput_connect(hid)) {
> +		matched = kmalloc(sizeof(struct matched_device), GFP_KERNEL);
> +		if (matched) {
> +			matched->intf = intf;
> +			down(&matched_lock);
> +			list_add(&matched->node, &matched_devices_list);
> +			up(&matched_lock);
> +			hidinput_simple_driver_bind(matched);
> +		}
>  		hid->claimed |= HID_CLAIMED_INPUT;
> +	}
>  	if (!hiddev_connect(hid))
>  		hid->claimed |= HID_CLAIMED_HIDDEV;
>  
> -	usb_set_intfdata(intf, hid);
> -
>  	if (!hid->claimed) {
>  		printk ("HID device not claimed by input or hiddev\n");
>  		hid_disconnect(intf);
> @@ -2108,6 +2291,8 @@ static int __init hid_init(void)
>  	retval = hiddev_init();
>  	if (retval)
>  		goto hiddev_init_fail;
> +	INIT_LIST_HEAD(&matched_devices_list);
> +	INIT_LIST_HEAD(&simple_drivers_list);
>  	retval = usb_register(&hid_driver);
>  	if (retval)
>  		goto usb_register_fail;
> @@ -2122,7 +2307,15 @@ hiddev_init_fail:
>  
>  static void __exit hid_exit(void)
>  {
> +	struct list_head *node, *tmp;
> +	struct matched_device *matched;
> +	
>  	usb_deregister(&hid_driver);
> +	list_for_each_safe(node, tmp, &matched_devices_list) {
> +		matched = list_entry(node, struct matched_device, node);
> +		list_del(&matched->node);
> +		kfree(matched);
> +	}
>  	hiddev_exit();
>  }
>  
> diff -Naurp linux-2.6.17.7/drivers/usb/input.orig/hid.h linux-2.6.17.7/drivers/usb/input/hid.h
> --- linux-2.6.17.7/drivers/usb/input.orig/hid.h	2006-07-25 11:36:01.000000000 +0800
> +++ linux-2.6.17.7/drivers/usb/input/hid.h	2006-08-03 15:23:15.000000000 +0800
> @@ -6,6 +6,7 @@
>   *
>   *  Copyright (c) 1999 Andreas Gal
>   *  Copyright (c) 2000-2001 Vojtech Pavlik
> + *  Copyright (c) 2006 Liyu     To support simple HID device.
>   */
>  
>  /*
> @@ -380,8 +381,11 @@ struct hid_input {
>  	struct list_head list;
>  	struct hid_report *report;
>  	struct input_dev *input;
> +	void *private;
>  };
>  
> +struct hidinput_simple_driver;
> +
>  struct hid_device {							/* device report descriptor */
>  	 __u8 *rdesc;
>  	unsigned rsize;
> @@ -445,6 +449,8 @@ struct hid_device {							/* device repo
>  	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
>  			unsigned int type, unsigned int code, int value);
>  
> +	struct hidinput_simple_driver *simple;
> +
>  #ifdef CONFIG_USB_HIDINPUT_POWERBOOK
>  	unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
>  	unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
> diff -Naurp linux-2.6.17.7/drivers/usb/input.orig/hid-input.c linux-2.6.17.7/drivers/usb/input/hid-input.c
> --- linux-2.6.17.7/drivers/usb/input.orig/hid-input.c	2006-07-25 11:36:01.000000000 +0800
> +++ linux-2.6.17.7/drivers/usb/input/hid-input.c	2006-08-03 17:26:48.000000000 +0800
> @@ -2,6 +2,7 @@
>   * $Id: hid-input.c,v 1.2 2002/04/23 00:59:25 rdamazio Exp $
>   *
>   *  Copyright (c) 2000-2001 Vojtech Pavlik
> + *  Copyright (c) 2006 Liyu <[email protected]>  HID simple driver interface
>   *
>   *  USB HID to Linux Input mapping
>   */
> @@ -36,6 +37,7 @@
>  #undef DEBUG
>  
>  #include "hid.h"
> +#include "hid-simple.h"
>  
>  #define unk	KEY_UNKNOWN
>  
> @@ -63,6 +65,8 @@ static const struct {
>  	__s32 y;
>  }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
>  
> +typedef void (*do_usage_t)(struct hid_field *, struct hid_usage *);
> +
>  #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
>  #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
>  #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
> @@ -849,6 +853,245 @@ int hidinput_connect(struct hid_device *
>  	return 0;
>  }
>  
> +int
> +hidinput_simple_driver_init(struct hidinput_simple_driver *drv)
> +{
> +	if (unlikely(!drv))
> +		return -EINVAL;
> +	INIT_LIST_HEAD(&drv->node);
> +	INIT_LIST_HEAD(&drv->raw_devices);
> +	drv->flags = 0;
> +	return 0;
> +}
> +
> +#define SET_BIT (1)
> +#define CLR_BIT (0)
> +static void inline
> +hidinput_simple_configure_one_usage(int op, struct input_dev *input, struct hid_usage *usage, struct usage_block *usage_block)
> +{
> +	unsigned long *bits;
> +
> +	switch (usage_block->event) {
> +		case EV_KEY:
> +			bits = input->keybit;	break;
> +		case EV_REL:
> +			bits = input->relbit;	break;
> +		case EV_ABS:
> +			bits = input->relbit;	break;
> +		case EV_MSC:
> +			bits = input->absbit;	break;
> +		case EV_SW:
> +			bits = input->swbit;	break;
> +		case EV_LED:
> +			bits = input->ledbit;	break;
> +		case EV_SND:
> +			bits = input->sndbit;	break;
> +		case EV_FF:
> +			bits = input->ffbit;	break;
> +		default:
> +			return;
> +	}
> +
> +	if (SET_BIT == op) {
> +		usage->code = usage_block->code;
> +		usage->type = usage_block->event;
> +		set_bit(usage_block->code, bits);
> +	}
> +	else if (CLR_BIT == op) {
> +		usage->code = 0;
> +		usage->type = 0;
> +		clear_bit(usage_block->code, bits);
> +	}
> +}
> +
> +static do_usage_t
> +hidinput_simple_driver_configure_usage_prep(struct hid_device *hid, int *op)
> +{
> +	do_usage_t do_usage;
> +
> +	if (!hid->simple)
> +		return NULL;
> +
> +	if (hid->simple->flags & HIDINPUT_SIMPLE_SETUP_USAGE) {
> +		do_usage = hid->simple->setup_usage;
> +		*op = SET_BIT;
> +	}
> +	else {
> +		do_usage = hid->simple->clear_usage;
> +		*op = CLR_BIT;
> +	}
> +	return do_usage;
> +}
> +
> +static void
> +__hidinput_simple_driver_configure_usage(int op,
> +					struct hid_field *field,
> +					struct hid_usage *hid_usage)
> +{
> +	struct input_dev *input = field->hidinput->input;
> +	struct hid_device *hid = input->private;
> +	struct usage_block *usage_block;
> +	struct usage_page_block *page_block;
> +	int page;
> +	int usage;
> +	
> +	page = (hid_usage->hid & HID_USAGE_PAGE);
> +	usage = (hid_usage->hid & HID_USAGE);
> +	page_block = hid->simple->usage_page_table;
> +
> +	for (;page_block && page_block->usage_blockes;page_block++) {
> +		if (page_block->page != page)
> +			continue;
> +		usage_block = page_block->usage_blockes;
> +		for (; usage_block && usage_block->usage; usage_block++) {
> +			if (usage_block->usage != usage)
> +				continue;
> +			hidinput_simple_configure_one_usage(op, input, 
> +						hid_usage, usage_block);
> +		}
> +	}
> +}
> +
> +/*
> + *  To give one simple device a configure usage chance.
> + *  The framework of this function come from hidinput_connect()
> + */
> +void hidinput_simple_driver_configure_usage(struct hid_device *hid)
> +{
> +	struct hid_report *report;
> +	int i, j, k;
> +	do_usage_t do_usage;
> +	int op;
> +
> +	if (!hid->simple)
> +		return;
> +
> +	do_usage = hidinput_simple_driver_configure_usage_prep(hid, &op);
> +
> +	for (i = 0; i < hid->maxcollection; i++)
> +		if (hid->collection[i].type == HID_COLLECTION_APPLICATION ||
> +			hid->collection[i].type==HID_COLLECTION_PHYSICAL)
> +			if (IS_INPUT_APPLICATION(hid->collection[i].usage))
> +				break;
> +
> +	if (i == hid->maxcollection)
> +		return;
> +
> +	for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++)
> +		list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
> +			if (!report->maxfield)
> +				continue;
> +
> +			for (i = 0; i < report->maxfield; i++)
> +				for (j = 0; j < report->field[i]->maxusage; j++) {
> +					__hidinput_simple_driver_configure_usage(
> +									op,
> +							report->field[i],
> +						report->field[i]->usage + j);

this piece is ugly.

> +					if (do_usage)
> +						do_usage(report->field[i],
> +						report->field[i]->usage + j);
> +				}
> +		}
> +
> +	return;
> +}
> +#undef SET_BIT
> +#undef CLR_BIT
> +
> +int

unbsd this and others.

regards,
-- 
<a href="http://www.fi.muni.cz/~xslaby/";>Jiri Slaby</a>
faculty of informatics, masaryk university, brno, cz
e-mail: jirislaby gmail com, gpg pubkey fingerprint:
B674 9967 0407 CE62 ACC8  22A0 32CC 55C3 39D4 7A7E
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux