xref: /minix3/minix/lib/libdevman/usb.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1433d6423SLionel Sambuc #include <errno.h>
2433d6423SLionel Sambuc #include <stdio.h>
3433d6423SLionel Sambuc #include <stdlib.h>
4433d6423SLionel Sambuc #include <string.h>
5433d6423SLionel Sambuc #include <minix/config.h>
6433d6423SLionel Sambuc #include <minix/const.h>
7433d6423SLionel Sambuc #include <minix/devman.h>
8433d6423SLionel Sambuc #include <minix/usb.h>
9433d6423SLionel Sambuc #include <minix/sysutil.h>
10433d6423SLionel Sambuc 
11433d6423SLionel Sambuc #include "local.h"
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc #define CHECKOUTOFMEM(ptr) if(ptr == NULL) \
14433d6423SLionel Sambuc                                panic("Out of memory! (%s, line %d)", \
15433d6423SLionel Sambuc 							     __FILE__, __LINE__)
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc 
18433d6423SLionel Sambuc static int (*bind_cb) (struct devman_usb_bind_cb_data *data, endpoint_t ep);
19433d6423SLionel Sambuc static int (*unbind_cb) (struct devman_usb_bind_cb_data *data, endpoint_t ep);
20433d6423SLionel Sambuc 
21433d6423SLionel Sambuc /****************************************************************************
22433d6423SLionel Sambuc  *    devman_usb_add_attr                                                   *
23433d6423SLionel Sambuc  ***************************************************************************/
24433d6423SLionel Sambuc static void
devman_usb_add_attr(struct devman_dev * dev,const char * name,const char * data)25433d6423SLionel Sambuc devman_usb_add_attr
2665f76edbSDavid van Moolenbroek (struct devman_dev *dev, const char *name, const char *data)
27433d6423SLionel Sambuc {
28433d6423SLionel Sambuc 	struct devman_static_attribute *attr = (struct devman_static_attribute *)
29433d6423SLionel Sambuc 	    malloc(sizeof(struct devman_static_attribute));
30433d6423SLionel Sambuc 
31433d6423SLionel Sambuc 	CHECKOUTOFMEM(attr);
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc 	attr->name = malloc((strlen(name)+1)*sizeof(char));
34433d6423SLionel Sambuc 	memcpy(attr->name, name, (strlen(name)+1));
35433d6423SLionel Sambuc 
36433d6423SLionel Sambuc 	attr->data = malloc((strlen(data)+1)*sizeof(char));
37433d6423SLionel Sambuc 	memcpy(attr->data, data, (strlen(data)+1));
38433d6423SLionel Sambuc 	TAILQ_INSERT_TAIL(&dev->attrs, attr, list);
39433d6423SLionel Sambuc }
40433d6423SLionel Sambuc 
41433d6423SLionel Sambuc /****************************************************************************
42433d6423SLionel Sambuc  *    add_device_attributes                                                 *
43433d6423SLionel Sambuc  ***************************************************************************/
44433d6423SLionel Sambuc static void
add_device_attributes(struct devman_usb_dev * udev)45433d6423SLionel Sambuc add_device_attributes
46433d6423SLionel Sambuc (struct devman_usb_dev *udev)
47433d6423SLionel Sambuc {
48433d6423SLionel Sambuc 	int ret;
49433d6423SLionel Sambuc 	char data[32];
50433d6423SLionel Sambuc 
51*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%02x",udev->desc->bDeviceClass);
52433d6423SLionel Sambuc 	if (ret < 0) {
53*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
54433d6423SLionel Sambuc 	}
55433d6423SLionel Sambuc 	devman_usb_add_attr(udev->dev, "bDeviceClass", data);
56433d6423SLionel Sambuc 
57*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%02x",udev->desc->bDeviceSubClass);
58433d6423SLionel Sambuc 	if (ret < 0) {
59*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
60433d6423SLionel Sambuc 	}
61433d6423SLionel Sambuc 	devman_usb_add_attr(udev->dev, "bDeviceSubClass", data);
62433d6423SLionel Sambuc 
63*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%02x",udev->desc->bDeviceProtocol);
64433d6423SLionel Sambuc 	if (ret < 0) {
65*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
66433d6423SLionel Sambuc 	}
67433d6423SLionel Sambuc 	devman_usb_add_attr(udev->dev, "bDeviceProtocol", data);
68433d6423SLionel Sambuc 
69*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%04x",UGETW(udev->desc->idVendor));
70433d6423SLionel Sambuc 	if (ret < 0) {
71*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
72433d6423SLionel Sambuc 	}
73433d6423SLionel Sambuc 	devman_usb_add_attr(udev->dev, "idVendor", data);
74433d6423SLionel Sambuc 
75*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%04x",UGETW(udev->desc->idProduct));
76433d6423SLionel Sambuc 	if (ret < 0) {
77*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
78433d6423SLionel Sambuc 	}
79433d6423SLionel Sambuc 	devman_usb_add_attr(udev->dev, "idProduct", data);
80433d6423SLionel Sambuc 
81433d6423SLionel Sambuc 	if (udev->product)
82433d6423SLionel Sambuc 		devman_usb_add_attr(udev->dev, "Product", udev->product);
83433d6423SLionel Sambuc 	if (udev->manufacturer)
84433d6423SLionel Sambuc 		devman_usb_add_attr(udev->dev, "Manufacturer", udev->manufacturer);
85433d6423SLionel Sambuc 	if (udev->serial)
86433d6423SLionel Sambuc 		devman_usb_add_attr(udev->dev, "SerialNumber", udev->serial);
87433d6423SLionel Sambuc 	devman_usb_add_attr(udev->dev, "dev_type", "USB_DEV");
88433d6423SLionel Sambuc }
89433d6423SLionel Sambuc 
90433d6423SLionel Sambuc /****************************************************************************
91433d6423SLionel Sambuc  *     add_interface_attributes                                             *
92433d6423SLionel Sambuc  ***************************************************************************/
93433d6423SLionel Sambuc static void
add_interface_attributes(struct devman_usb_interface * intf)94433d6423SLionel Sambuc add_interface_attributes
95433d6423SLionel Sambuc (struct devman_usb_interface *intf)
96433d6423SLionel Sambuc {
97433d6423SLionel Sambuc 	int ret;
98433d6423SLionel Sambuc 	char data[32];
99433d6423SLionel Sambuc 
100*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%02x",intf->desc->bInterfaceNumber);
101433d6423SLionel Sambuc 	if (ret < 0) {
102*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
103433d6423SLionel Sambuc 	}
104433d6423SLionel Sambuc 	devman_usb_add_attr(intf->dev, "bInterfaceNumber", data);
105433d6423SLionel Sambuc 
106*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%02x",intf->desc->bAlternateSetting);
107433d6423SLionel Sambuc 	if (ret < 0) {
108*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
109433d6423SLionel Sambuc 	}
110433d6423SLionel Sambuc 	devman_usb_add_attr(intf->dev, "bAlternateSetting", data);
111433d6423SLionel Sambuc 
112*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%02x",intf->desc->bNumEndpoints);
113433d6423SLionel Sambuc 	if (ret < 0) {
114*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
115433d6423SLionel Sambuc 	}
116433d6423SLionel Sambuc 	devman_usb_add_attr(intf->dev, "bNumEndpoints", data);
117433d6423SLionel Sambuc 
118*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%02x",intf->desc->bInterfaceClass);
119433d6423SLionel Sambuc 	if (ret < 0) {
120*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
121433d6423SLionel Sambuc 	}
122433d6423SLionel Sambuc 	devman_usb_add_attr(intf->dev, "bInterfaceClass", data);
123433d6423SLionel Sambuc 
124*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%02x",intf->desc->bInterfaceSubClass);
125433d6423SLionel Sambuc 	if (ret < 0) {
126*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
127433d6423SLionel Sambuc 	}
128433d6423SLionel Sambuc 	devman_usb_add_attr(intf->dev, "bInterfaceSubClass", data);
129433d6423SLionel Sambuc 
130*0a6a1f1dSLionel Sambuc 	ret = snprintf(data,sizeof(data),"0x%02x",intf->desc->bInterfaceProtocol);
131433d6423SLionel Sambuc 	if (ret < 0) {
132*0a6a1f1dSLionel Sambuc 		panic("add_device_attributes: snprintf failed");
133433d6423SLionel Sambuc 	}
134433d6423SLionel Sambuc 	devman_usb_add_attr(intf->dev, "bInterfaceProtocol", data);
135433d6423SLionel Sambuc 
136433d6423SLionel Sambuc 	devman_usb_add_attr(intf->dev, "dev_type", "USB_INTF");
137433d6423SLionel Sambuc }
138433d6423SLionel Sambuc 
139433d6423SLionel Sambuc 
140433d6423SLionel Sambuc /****************************************************************************
141433d6423SLionel Sambuc  *      devman_usb_device_new                                               *
142433d6423SLionel Sambuc  ***************************************************************************/
143433d6423SLionel Sambuc struct devman_usb_dev*
devman_usb_device_new(int dev_id)144433d6423SLionel Sambuc devman_usb_device_new
145433d6423SLionel Sambuc (int dev_id)
146433d6423SLionel Sambuc {
147433d6423SLionel Sambuc 	struct devman_usb_dev *udev = NULL;
148433d6423SLionel Sambuc 	struct devman_dev * dev = NULL;
149433d6423SLionel Sambuc 
150433d6423SLionel Sambuc 	udev = (struct devman_usb_dev *) malloc(sizeof(struct devman_usb_dev));
151433d6423SLionel Sambuc 
152433d6423SLionel Sambuc 	CHECKOUTOFMEM(udev);
153433d6423SLionel Sambuc 
154433d6423SLionel Sambuc 	/* allocate device */
155433d6423SLionel Sambuc 	dev = (struct devman_dev *) malloc(sizeof(struct devman_dev));
156433d6423SLionel Sambuc 
157433d6423SLionel Sambuc 	CHECKOUTOFMEM(dev);
158433d6423SLionel Sambuc 
159433d6423SLionel Sambuc 	udev->dev_id = dev_id;
160433d6423SLionel Sambuc 	udev->dev    = dev;
161433d6423SLionel Sambuc 
162433d6423SLionel Sambuc 	dev->parent_dev_id = 0; /* For now add it directly to the root dev */
163433d6423SLionel Sambuc 
164433d6423SLionel Sambuc 	snprintf(dev->name, DEVMAN_DEV_NAME_LEN, "USB%d", dev_id);
165433d6423SLionel Sambuc 
166433d6423SLionel Sambuc 	TAILQ_INIT(&dev->attrs);
167433d6423SLionel Sambuc 
168433d6423SLionel Sambuc 	return udev;
169433d6423SLionel Sambuc }
170433d6423SLionel Sambuc 
171433d6423SLionel Sambuc /****************************************************************************
172433d6423SLionel Sambuc  *     devman_usb_device_delete                                             *
173433d6423SLionel Sambuc  ***************************************************************************/
devman_usb_device_delete(struct devman_usb_dev * udev)174433d6423SLionel Sambuc void devman_usb_device_delete(struct devman_usb_dev *udev)
175433d6423SLionel Sambuc {
176433d6423SLionel Sambuc 	int i;
177433d6423SLionel Sambuc 	struct devman_static_attribute *attr,*temp;
178433d6423SLionel Sambuc 
179433d6423SLionel Sambuc 
180433d6423SLionel Sambuc 	for (i=0; i < udev->intf_count; i++) {
181433d6423SLionel Sambuc 		TAILQ_FOREACH_SAFE(attr, &udev->interfaces[i].dev->attrs, list, temp)
182433d6423SLionel Sambuc 		{
183433d6423SLionel Sambuc 			free(attr->name);
184433d6423SLionel Sambuc 			free(attr->data);
185433d6423SLionel Sambuc 			free(attr);
186433d6423SLionel Sambuc 		}
187433d6423SLionel Sambuc 		free(udev->interfaces[i].dev);
188433d6423SLionel Sambuc 	}
189433d6423SLionel Sambuc 
190433d6423SLionel Sambuc 	TAILQ_FOREACH_SAFE(attr, &udev->dev->attrs, list, temp) {
191433d6423SLionel Sambuc 		free(attr->name);
192433d6423SLionel Sambuc 		free(attr->data);
193433d6423SLionel Sambuc 		free(attr);
194433d6423SLionel Sambuc 	}
195433d6423SLionel Sambuc 
196433d6423SLionel Sambuc 	free(udev->dev);
197433d6423SLionel Sambuc 	free(udev);
198433d6423SLionel Sambuc }
199433d6423SLionel Sambuc 
devman_usb_bind_cb(void * data,endpoint_t ep)200433d6423SLionel Sambuc static int devman_usb_bind_cb(void *data, endpoint_t ep) {
201433d6423SLionel Sambuc 	if (bind_cb) {
202433d6423SLionel Sambuc 		return bind_cb((struct devman_usb_bind_cb_data *) data, ep);
203433d6423SLionel Sambuc 	} else {
204433d6423SLionel Sambuc 		return ENODEV;
205433d6423SLionel Sambuc 	}
206433d6423SLionel Sambuc }
207433d6423SLionel Sambuc 
devman_usb_unbind_cb(void * data,endpoint_t ep)208433d6423SLionel Sambuc static int devman_usb_unbind_cb(void *data, endpoint_t ep) {
209433d6423SLionel Sambuc 	if (unbind_cb) {
210433d6423SLionel Sambuc 		return unbind_cb((struct devman_usb_bind_cb_data *) data, ep);
211433d6423SLionel Sambuc 	} else {
212433d6423SLionel Sambuc 		return ENODEV;
213433d6423SLionel Sambuc 	}
214433d6423SLionel Sambuc }
215433d6423SLionel Sambuc 
216433d6423SLionel Sambuc /****************************************************************************
217433d6423SLionel Sambuc  *     devman_usb_device_add                                                *
218433d6423SLionel Sambuc  ***************************************************************************/
devman_usb_device_add(struct devman_usb_dev * dev)219433d6423SLionel Sambuc int devman_usb_device_add(struct devman_usb_dev *dev)
220433d6423SLionel Sambuc {
221433d6423SLionel Sambuc 	int i,res = 0;
222433d6423SLionel Sambuc 	add_device_attributes(dev);
223433d6423SLionel Sambuc 
224433d6423SLionel Sambuc 	/* add the USB device */
225433d6423SLionel Sambuc 	dev->cb_data.dev_id    = dev->dev_id;
226433d6423SLionel Sambuc 	dev->cb_data.interface = -1;
227433d6423SLionel Sambuc 
228433d6423SLionel Sambuc 	dev->dev->bind_cb   = devman_usb_bind_cb;
229433d6423SLionel Sambuc 	dev->dev->unbind_cb = devman_usb_unbind_cb;
230433d6423SLionel Sambuc 	dev->dev->data       = &dev->cb_data;
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc 	res = devman_add_device(dev->dev);
233433d6423SLionel Sambuc 
234433d6423SLionel Sambuc 	if (res != 0) {
235433d6423SLionel Sambuc 		panic("devman_usb_device_add(): devman_add_device failed.");
236433d6423SLionel Sambuc 	}
237433d6423SLionel Sambuc 
238433d6423SLionel Sambuc 	/* add the USB interfaces */
239433d6423SLionel Sambuc 	for (i=0; i < dev->intf_count; i++) {
240433d6423SLionel Sambuc 		/* prepare */
241433d6423SLionel Sambuc 		dev->interfaces[i].dev =
242433d6423SLionel Sambuc 		    (struct devman_dev *) malloc(sizeof(struct devman_dev));
243433d6423SLionel Sambuc 		CHECKOUTOFMEM(dev->interfaces[i].dev);
244433d6423SLionel Sambuc 
245433d6423SLionel Sambuc 		TAILQ_INIT(&dev->interfaces[i].dev->attrs);
246433d6423SLionel Sambuc 		snprintf(dev->interfaces[i].dev->name, DEVMAN_DEV_NAME_LEN,
247433d6423SLionel Sambuc 		    "intf%d", i);
248433d6423SLionel Sambuc 
249433d6423SLionel Sambuc 		add_interface_attributes(&dev->interfaces[i]);
250433d6423SLionel Sambuc 
251433d6423SLionel Sambuc 		dev->interfaces[i].dev->parent_dev_id = dev->dev->dev_id;
252433d6423SLionel Sambuc 
253433d6423SLionel Sambuc 
254433d6423SLionel Sambuc 		dev->interfaces[i].cb_data.dev_id    = dev->dev_id;
255433d6423SLionel Sambuc 		dev->interfaces[i].cb_data.interface =
256433d6423SLionel Sambuc 		    dev->interfaces[i].desc->bInterfaceNumber;
257433d6423SLionel Sambuc 
258433d6423SLionel Sambuc 		dev->interfaces[i].dev->bind_cb   = devman_usb_bind_cb;
259433d6423SLionel Sambuc 		dev->interfaces[i].dev->unbind_cb = devman_usb_unbind_cb;
260433d6423SLionel Sambuc 		dev->interfaces[i].dev->data      = &dev->interfaces[i].cb_data;
261433d6423SLionel Sambuc 
262433d6423SLionel Sambuc 		/* add */
263433d6423SLionel Sambuc 		res = devman_add_device(dev->interfaces[i].dev);
264433d6423SLionel Sambuc 
265433d6423SLionel Sambuc 		if (res != 0) {
266433d6423SLionel Sambuc 			panic("devman_usb_device_add(): devman_add_device failed.");
267433d6423SLionel Sambuc 		}
268433d6423SLionel Sambuc 	}
269433d6423SLionel Sambuc 
270433d6423SLionel Sambuc 	return res;
271433d6423SLionel Sambuc }
272433d6423SLionel Sambuc 
273433d6423SLionel Sambuc /****************************************************************************
274433d6423SLionel Sambuc  *     devman_usb_device_remove                                             *
275433d6423SLionel Sambuc  ***************************************************************************/
devman_usb_device_remove(struct devman_usb_dev * dev)276433d6423SLionel Sambuc int devman_usb_device_remove(struct devman_usb_dev *dev)
277433d6423SLionel Sambuc {
278433d6423SLionel Sambuc 	int i, res = 0;
279433d6423SLionel Sambuc 
280433d6423SLionel Sambuc 	for (i=0; i < dev->intf_count; i++) {
281433d6423SLionel Sambuc 
282433d6423SLionel Sambuc 		res = devman_del_device(dev->interfaces[i].dev);
283433d6423SLionel Sambuc 
284433d6423SLionel Sambuc 		if (res != 0) {
285433d6423SLionel Sambuc 			panic("devman_usb_device_remove(): devman_del_device failed.");
286433d6423SLionel Sambuc 		}
287433d6423SLionel Sambuc 	}
288433d6423SLionel Sambuc 	res = devman_del_device(dev->dev);
289433d6423SLionel Sambuc 	return res;
290433d6423SLionel Sambuc }
291433d6423SLionel Sambuc 
292433d6423SLionel Sambuc /****************************************************************************
293433d6423SLionel Sambuc  *     devman_usb_init                                                      *
294433d6423SLionel Sambuc  ***************************************************************************/
devman_usb_init(int (* _bind_cb)(struct devman_usb_bind_cb_data * data,endpoint_t ep),int (* _unbind_cb)(struct devman_usb_bind_cb_data * data,endpoint_t ep))295433d6423SLionel Sambuc void devman_usb_init
296433d6423SLionel Sambuc (int (*_bind_cb)   (struct devman_usb_bind_cb_data *data, endpoint_t ep),
297433d6423SLionel Sambuc  int (*_unbind_cb) (struct devman_usb_bind_cb_data *data, endpoint_t ep))
298433d6423SLionel Sambuc {
299433d6423SLionel Sambuc 	bind_cb   = _bind_cb;
300433d6423SLionel Sambuc 	unbind_cb = _unbind_cb;
301433d6423SLionel Sambuc }
302