[PATCH] pcmcia: device and driver matching
The actual matching of pcmcia drivers and pcmcia devices. The original version of this was written by David Woodhouse. Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
3ee1393752
commit
1ad275e3e7
|
@ -101,6 +101,9 @@ struct pcmcia_bus_socket {
|
|||
u8 device_count; /* the number of devices, used
|
||||
* only internally and subject
|
||||
* to incorrectness and change */
|
||||
|
||||
u8 device_add_pending;
|
||||
struct work_struct device_add;
|
||||
};
|
||||
static spinlock_t pcmcia_dev_list_lock;
|
||||
|
||||
|
@ -512,6 +515,10 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns
|
|||
|
||||
down(&device_add_lock);
|
||||
|
||||
/* max of 2 devices per card */
|
||||
if (s->device_count == 2)
|
||||
goto err_put;
|
||||
|
||||
p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
|
||||
if (!p_dev)
|
||||
goto err_put;
|
||||
|
@ -537,6 +544,8 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns
|
|||
list_add_tail(&p_dev->socket_device_list, &s->devices_list);
|
||||
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
|
||||
|
||||
pcmcia_device_query(p_dev);
|
||||
|
||||
if (device_register(&p_dev->dev)) {
|
||||
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
|
||||
list_del(&p_dev->socket_device_list);
|
||||
|
@ -591,14 +600,123 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
|
|||
}
|
||||
|
||||
|
||||
static void pcmcia_delayed_add_pseudo_device(void *data)
|
||||
{
|
||||
struct pcmcia_bus_socket *s = data;
|
||||
pcmcia_device_add(s, 0);
|
||||
s->device_add_pending = 0;
|
||||
}
|
||||
|
||||
static inline void pcmcia_add_pseudo_device(struct pcmcia_bus_socket *s)
|
||||
{
|
||||
if (!s->device_add_pending) {
|
||||
schedule_work(&s->device_add);
|
||||
s->device_add_pending = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static inline int pcmcia_devmatch(struct pcmcia_device *dev,
|
||||
struct pcmcia_device_id *did)
|
||||
{
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID) {
|
||||
if ((!dev->has_manf_id) || (dev->manf_id != did->manf_id))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID) {
|
||||
if ((!dev->has_card_id) || (dev->card_id != did->card_id))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION) {
|
||||
if (dev->func != did->function)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1) {
|
||||
if (!dev->prod_id[0])
|
||||
return 0;
|
||||
if (strcmp(did->prod_id[0], dev->prod_id[0]))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2) {
|
||||
if (!dev->prod_id[1])
|
||||
return 0;
|
||||
if (strcmp(did->prod_id[1], dev->prod_id[1]))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3) {
|
||||
if (!dev->prod_id[2])
|
||||
return 0;
|
||||
if (strcmp(did->prod_id[2], dev->prod_id[2]))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4) {
|
||||
if (!dev->prod_id[3])
|
||||
return 0;
|
||||
if (strcmp(did->prod_id[3], dev->prod_id[3]))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) {
|
||||
/* handle pseudo multifunction devices:
|
||||
* there are at most two pseudo multifunction devices.
|
||||
* if we're matching against the first, schedule a
|
||||
* call which will then check whether there are two
|
||||
* pseudo devices, and if not, add the second one.
|
||||
*/
|
||||
if (dev->device_no == 0)
|
||||
pcmcia_add_pseudo_device(dev->socket->pcmcia);
|
||||
|
||||
if (dev->device_no != did->device_no)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) {
|
||||
if ((!dev->has_func_id) || (dev->func_id != did->func_id))
|
||||
return 0;
|
||||
|
||||
/* if this is a pseudo-multi-function device,
|
||||
* we need explicit matches */
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO)
|
||||
return 0;
|
||||
if (dev->device_no)
|
||||
return 0;
|
||||
|
||||
/* also, FUNC_ID matching needs to be activated by userspace
|
||||
* after it has re-checked that there is no possible module
|
||||
* with a prod_id/manf_id/card_id match.
|
||||
*/
|
||||
if (!dev->allow_func_id_match)
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev->dev.driver_data = (void *) did;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) {
|
||||
struct pcmcia_device * p_dev = to_pcmcia_dev(dev);
|
||||
struct pcmcia_driver * p_drv = to_pcmcia_drv(drv);
|
||||
struct pcmcia_device_id *did = p_drv->id_table;
|
||||
|
||||
/* matching by cardmgr */
|
||||
if (p_dev->cardmgr == p_drv)
|
||||
return 1;
|
||||
|
||||
while (did && did->match_flags) {
|
||||
if (pcmcia_devmatch(p_dev, did))
|
||||
return 1;
|
||||
did++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -922,7 +1040,9 @@ static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info)
|
|||
rescan:
|
||||
p_dev->cardmgr = p_drv;
|
||||
|
||||
pcmcia_device_query(p_dev);
|
||||
/* if a driver is already running, we can abort */
|
||||
if (p_dev->dev.driver)
|
||||
goto err_put_module;
|
||||
|
||||
/*
|
||||
* Prevent this racing with a card insertion.
|
||||
|
@ -1595,6 +1715,7 @@ static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev)
|
|||
|
||||
init_waitqueue_head(&s->queue);
|
||||
INIT_LIST_HEAD(&s->devices_list);
|
||||
INIT_WORK(&s->device_add, pcmcia_delayed_add_pseudo_device, s);
|
||||
|
||||
/* Set up hotline to Card Services */
|
||||
s->callback.owner = THIS_MODULE;
|
||||
|
|
|
@ -175,4 +175,37 @@ struct serio_device_id {
|
|||
};
|
||||
|
||||
|
||||
/* PCMCIA */
|
||||
|
||||
struct pcmcia_device_id {
|
||||
__u16 match_flags;
|
||||
|
||||
__u16 manf_id;
|
||||
__u16 card_id;
|
||||
|
||||
__u8 func_id;
|
||||
|
||||
/* for real multi-function devices */
|
||||
__u8 function;
|
||||
|
||||
/* for pseude multi-function devices */
|
||||
__u8 device_no;
|
||||
|
||||
const char * prod_id[4];
|
||||
__u32 prod_id_hash[4];
|
||||
|
||||
/* not matched against */
|
||||
kernel_ulong_t driver_info;
|
||||
};
|
||||
|
||||
#define PCMCIA_DEV_ID_MATCH_MANF_ID 0x0001
|
||||
#define PCMCIA_DEV_ID_MATCH_CARD_ID 0x0002
|
||||
#define PCMCIA_DEV_ID_MATCH_FUNC_ID 0x0004
|
||||
#define PCMCIA_DEV_ID_MATCH_FUNCTION 0x0008
|
||||
#define PCMCIA_DEV_ID_MATCH_PROD_ID1 0x0010
|
||||
#define PCMCIA_DEV_ID_MATCH_PROD_ID2 0x0020
|
||||
#define PCMCIA_DEV_ID_MATCH_PROD_ID3 0x0040
|
||||
#define PCMCIA_DEV_ID_MATCH_PROD_ID4 0x0080
|
||||
#define PCMCIA_DEV_ID_MATCH_DEVICE_NO 0x0100
|
||||
|
||||
#endif /* LINUX_MOD_DEVICETABLE_H */
|
||||
|
|
175
include/pcmcia/device_id.h
Normal file
175
include/pcmcia/device_id.h
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (2003-2004) Dominik Brodowski <linux@brodo.de>
|
||||
* David Woodhouse
|
||||
*
|
||||
* License: GPL v2
|
||||
*/
|
||||
|
||||
#define PCMCIA_DEVICE_MANF_CARD(manf, card) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
|
||||
PCMCIA_DEV_ID_MATCH_CARD_ID, \
|
||||
.manf_id = (manf), \
|
||||
.card_id = (card), }
|
||||
|
||||
#define PCMCIA_DEVICE_FUNC_ID(func) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_FUNC_ID, \
|
||||
.func_id = (func), }
|
||||
|
||||
#define PCMCIA_DEVICE_PROD_ID1(v1, vh1) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1, \
|
||||
.prod_id = { (v1), NULL, NULL, NULL }, \
|
||||
.prod_id_hash = { (vh1), 0, 0, 0 }, }
|
||||
|
||||
#define PCMCIA_DEVICE_PROD_ID2(v2, vh2) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2, \
|
||||
.prod_id = { NULL, (v2), NULL, NULL }, \
|
||||
.prod_id_hash = { 0, (vh2), 0, 0 }, }
|
||||
|
||||
#define PCMCIA_DEVICE_PROD_ID12(v1, v2, vh1, vh2) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID2, \
|
||||
.prod_id = { (v1), (v2), NULL, NULL }, \
|
||||
.prod_id_hash = { (vh1), (vh2), 0, 0 }, }
|
||||
|
||||
#define PCMCIA_DEVICE_PROD_ID13(v1, v3, vh1, vh3) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID3, \
|
||||
.prod_id = { (v1), NULL, (v3), NULL }, \
|
||||
.prod_id_hash = { (vh1), 0, (vh3), 0 }, }
|
||||
|
||||
#define PCMCIA_DEVICE_PROD_ID14(v1, v4, vh1, vh4) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID4, \
|
||||
.prod_id = { (v1), NULL, NULL, (v4) }, \
|
||||
.prod_id_hash = { (vh1), 0, 0, (vh4) }, }
|
||||
|
||||
#define PCMCIA_DEVICE_PROD_ID123(v1, v2, v3, vh1, vh2, vh3) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID2| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID3, \
|
||||
.prod_id = { (v1), (v2), (v3), NULL },\
|
||||
.prod_id_hash = { (vh1), (vh2), (vh3), 0 }, }
|
||||
|
||||
#define PCMCIA_DEVICE_PROD_ID124(v1, v2, v4, vh1, vh2, vh4) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID2| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID4, \
|
||||
.prod_id = { (v1), (v2), NULL, (v4) }, \
|
||||
.prod_id_hash = { (vh1), (vh2), 0, (vh4) }, }
|
||||
|
||||
#define PCMCIA_DEVICE_PROD_ID134(v1, v3, v4, vh1, vh3, vh4) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID3| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID4, \
|
||||
.prod_id = { (v1), NULL, (v3), (v4) }, \
|
||||
.prod_id_hash = { (vh1), 0, (vh3), (vh4) }, }
|
||||
|
||||
#define PCMCIA_DEVICE_PROD_ID1234(v1, v2, v3, v4, vh1, vh2, vh3, vh4) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID2| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID3| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID4, \
|
||||
.prod_id = { (v1), (v2), (v3), (v4) }, \
|
||||
.prod_id_hash = { (vh1), (vh2), (vh3), (vh4) }, }
|
||||
|
||||
|
||||
/* multi-function devices */
|
||||
|
||||
#define PCMCIA_MFC_DEVICE_MANF_CARD(mfc, manf, card) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
|
||||
PCMCIA_DEV_ID_MATCH_CARD_ID| \
|
||||
PCMCIA_DEV_ID_MATCH_FUNCTION, \
|
||||
.manf_id = (manf), \
|
||||
.card_id = (card), \
|
||||
.function = (mfc), }
|
||||
|
||||
#define PCMCIA_MFC_DEVICE_PROD_ID1(mfc, v1, vh1) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_FUNCTION, \
|
||||
.prod_id = { (v1), NULL, NULL, NULL }, \
|
||||
.prod_id_hash = { (vh1), 0, 0, 0 }, \
|
||||
.function = (mfc), }
|
||||
|
||||
#define PCMCIA_MFC_DEVICE_PROD_ID2(mfc, v2, vh2) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2| \
|
||||
PCMCIA_DEV_ID_MATCH_FUNCTION, \
|
||||
.prod_id = { NULL, (v2), NULL, NULL }, \
|
||||
.prod_id_hash = { 0, (vh2), 0, 0 }, \
|
||||
.function = (mfc), }
|
||||
|
||||
#define PCMCIA_MFC_DEVICE_PROD_ID12(mfc, v1, v2, vh1, vh2) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID2| \
|
||||
PCMCIA_DEV_ID_MATCH_FUNCTION, \
|
||||
.prod_id = { (v1), (v2), NULL, NULL }, \
|
||||
.prod_id_hash = { (vh1), (vh2), 0, 0 }, \
|
||||
.function = (mfc), }
|
||||
|
||||
#define PCMCIA_MFC_DEVICE_PROD_ID13(mfc, v1, v3, vh1, vh3) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID3| \
|
||||
PCMCIA_DEV_ID_MATCH_FUNCTION, \
|
||||
.prod_id = { (v1), NULL, (v3), NULL }, \
|
||||
.prod_id_hash = { (vh1), 0, (vh3), 0 }, \
|
||||
.function = (mfc), }
|
||||
|
||||
#define PCMCIA_MFC_DEVICE_PROD_ID123(mfc, v1, v2, v3, vh1, vh2, vh3) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID2| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID3| \
|
||||
PCMCIA_DEV_ID_MATCH_FUNCTION, \
|
||||
.prod_id = { (v1), (v2), (v3), NULL },\
|
||||
.prod_id_hash = { (vh1), (vh2), (vh3), 0 }, \
|
||||
.function = (mfc), }
|
||||
|
||||
/* pseudo multi-function devices */
|
||||
|
||||
#define PCMCIA_PFC_DEVICE_MANF_CARD(mfc, manf, card) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
|
||||
PCMCIA_DEV_ID_MATCH_CARD_ID| \
|
||||
PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
|
||||
.manf_id = (manf), \
|
||||
.card_id = (card), \
|
||||
.device_no = (mfc), }
|
||||
|
||||
#define PCMCIA_PFC_DEVICE_PROD_ID1(mfc, v1, vh1) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
|
||||
.prod_id = { (v1), NULL, NULL, NULL }, \
|
||||
.prod_id_hash = { (vh1), 0, 0, 0 }, \
|
||||
.device_no = (mfc), }
|
||||
|
||||
#define PCMCIA_PFC_DEVICE_PROD_ID2(mfc, v2, vh2) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2| \
|
||||
PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
|
||||
.prod_id = { NULL, (v2), NULL, NULL }, \
|
||||
.prod_id_hash = { 0, (vh2), 0, 0 }, \
|
||||
.device_no = (mfc), }
|
||||
|
||||
#define PCMCIA_PFC_DEVICE_PROD_ID12(mfc, v1, v2, vh1, vh2) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID2| \
|
||||
PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
|
||||
.prod_id = { (v1), (v2), NULL, NULL }, \
|
||||
.prod_id_hash = { (vh1), (vh2), 0, 0 }, \
|
||||
.device_no = (mfc), }
|
||||
|
||||
#define PCMCIA_PFC_DEVICE_PROD_ID13(mfc, v1, v3, vh1, vh3) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID3| \
|
||||
PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
|
||||
.prod_id = { (v1), NULL, (v3), NULL }, \
|
||||
.prod_id_hash = { (vh1), 0, (vh3), 0 }, \
|
||||
.device_no = (mfc), }
|
||||
|
||||
#define PCMCIA_PFC_DEVICE_PROD_ID123(mfc, v1, v2, v3, vh1, vh2, vh3) { \
|
||||
.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID2| \
|
||||
PCMCIA_DEV_ID_MATCH_PROD_ID3| \
|
||||
PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
|
||||
.prod_id = { (v1), (v2), (v3), NULL },\
|
||||
.prod_id_hash = { (vh1), (vh2), (vh3), 0 }, \
|
||||
.device_no = (mfc), }
|
||||
|
||||
|
||||
#define PCMCIA_DEVICE_NULL { .match_flags = 0, }
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include <pcmcia/bulkmem.h>
|
||||
#include <pcmcia/cs_types.h>
|
||||
#include <pcmcia/device_id.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
typedef struct tuple_parse_t {
|
||||
tuple_t tuple;
|
||||
|
@ -135,6 +137,7 @@ struct pcmcia_driver {
|
|||
dev_link_t *(*attach)(void);
|
||||
void (*detach)(dev_link_t *);
|
||||
struct module *owner;
|
||||
struct pcmcia_device_id *id_table;
|
||||
struct device_driver drv;
|
||||
};
|
||||
|
||||
|
@ -173,7 +176,9 @@ struct pcmcia_device {
|
|||
u8 has_manf_id:1;
|
||||
u8 has_card_id:1;
|
||||
u8 has_func_id:1;
|
||||
u8 reserved:5;
|
||||
|
||||
u8 allow_func_id_match:1;
|
||||
u8 reserved:4;
|
||||
|
||||
u8 func_id;
|
||||
u16 manf_id;
|
||||
|
|
Loading…
Reference in a new issue