xref: /minix3/minix/lib/libusb/usb.c (revision 65f76edb8f568553a5b4386b0c4917803d47e97d)
1433d6423SLionel Sambuc #include <minix/config.h>
2433d6423SLionel Sambuc #include <minix/const.h>
3433d6423SLionel Sambuc #include <minix/usb.h>
4433d6423SLionel Sambuc #include <minix/com.h>
5433d6423SLionel Sambuc #include <minix/safecopies.h>
6433d6423SLionel Sambuc #include <minix/sysutil.h>
7433d6423SLionel Sambuc #include <minix/ds.h>
8433d6423SLionel Sambuc #include <stdlib.h>
9433d6423SLionel Sambuc #include <errno.h>
10433d6423SLionel Sambuc #include <string.h>
11433d6423SLionel Sambuc 
12433d6423SLionel Sambuc static struct usb_urb * pending_urbs = NULL;
13433d6423SLionel Sambuc static endpoint_t hcd_ep;
14433d6423SLionel Sambuc 
15*65f76edbSDavid van Moolenbroek static void _usb_urb_complete(struct usb_driver *ud, unsigned int urb_id);
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc /*****************************************************************************
18433d6423SLionel Sambuc  *         usb_send_urb                                                      *
19433d6423SLionel Sambuc  ****************************************************************************/
usb_send_urb(struct usb_urb * urb)20433d6423SLionel Sambuc int usb_send_urb(struct usb_urb* urb)
21433d6423SLionel Sambuc {
22433d6423SLionel Sambuc 	message msg;
23433d6423SLionel Sambuc 	int res;
24433d6423SLionel Sambuc 	cp_grant_id_t gid;
25433d6423SLionel Sambuc 	if (urb == NULL) {
26433d6423SLionel Sambuc 		return EINVAL;
27433d6423SLionel Sambuc 	}
28433d6423SLionel Sambuc 
29433d6423SLionel Sambuc 	if (hcd_ep == 0) {
30433d6423SLionel Sambuc 		return EINVAL;
31433d6423SLionel Sambuc 	}
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc 	/* setup grant */
34433d6423SLionel Sambuc 	gid = cpf_grant_direct(hcd_ep,(vir_bytes) &urb->dev_id,
35433d6423SLionel Sambuc 	         urb->urb_size - sizeof(void*),CPF_WRITE|CPF_READ);
36433d6423SLionel Sambuc 
37433d6423SLionel Sambuc 	if (gid == -1) {
38433d6423SLionel Sambuc 		printf("usb_send_urb: grant failed: "
39433d6423SLionel Sambuc 		      "cpf_grant_direct(%d,%p,%d)\n", hcd_ep, urb, urb->urb_size);
40433d6423SLionel Sambuc 		return EINVAL;
41433d6423SLionel Sambuc 	}
42433d6423SLionel Sambuc 
43433d6423SLionel Sambuc 	urb->gid = gid;
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc 	/* prepare message */
46433d6423SLionel Sambuc 	msg.m_type         = USB_RQ_SEND_URB;
47433d6423SLionel Sambuc 	msg.USB_GRANT_ID   = gid;
48433d6423SLionel Sambuc 	msg.USB_GRANT_SIZE = urb->urb_size-sizeof(void*);
49433d6423SLionel Sambuc 
50433d6423SLionel Sambuc 	/* send message */
51433d6423SLionel Sambuc 	res = ipc_sendrec(hcd_ep, &msg);
52433d6423SLionel Sambuc 
53433d6423SLionel Sambuc 	if (res != 0) {
54433d6423SLionel Sambuc 		panic("usb_send_urb: could not talk to hcd: %d", res);
55433d6423SLionel Sambuc 	}
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc 	if (msg.m_type != USB_REPLY) {
58433d6423SLionel Sambuc 		panic("usb_send_urb: got illegal response from hcd: %d", msg.m_type);
59433d6423SLionel Sambuc 	}
60433d6423SLionel Sambuc 
61433d6423SLionel Sambuc 	if (msg.USB_RESULT != 0) {
62433d6423SLionel Sambuc 		panic("usb_send_urb: hcd could not enqueue URB: %ld", msg.USB_RESULT);
63433d6423SLionel Sambuc 	}
64433d6423SLionel Sambuc 
65433d6423SLionel Sambuc 	/* everything ok, add urb to pending_urbs */
66433d6423SLionel Sambuc 	urb->urb_id = msg.USB_URB_ID;
67433d6423SLionel Sambuc 	urb->next = pending_urbs;
68433d6423SLionel Sambuc 	pending_urbs = urb;
69433d6423SLionel Sambuc 
70433d6423SLionel Sambuc 	/* done. */
71433d6423SLionel Sambuc 
72433d6423SLionel Sambuc 	/* (The HCD will send us a message when the URB is completed.) */
73433d6423SLionel Sambuc 
74433d6423SLionel Sambuc 	return res;
75433d6423SLionel Sambuc }
76433d6423SLionel Sambuc 
77433d6423SLionel Sambuc /*****************************************************************************
78433d6423SLionel Sambuc  *         usb_cancle_urb                                                    *
79433d6423SLionel Sambuc  ****************************************************************************/
usb_cancle_urb(struct usb_urb * urb)80433d6423SLionel Sambuc int usb_cancle_urb(struct usb_urb* urb)
81433d6423SLionel Sambuc {
82433d6423SLionel Sambuc 	int res;
83433d6423SLionel Sambuc 	message msg;
84433d6423SLionel Sambuc 
85433d6423SLionel Sambuc 	if (urb == NULL) {
86433d6423SLionel Sambuc 		panic("usb_send_urb: urb == NULL!");
87433d6423SLionel Sambuc 	}
88433d6423SLionel Sambuc 
89433d6423SLionel Sambuc 	if (urb->urb_id == USB_INVALID_URB_ID) {
90433d6423SLionel Sambuc 		return EINVAL;
91433d6423SLionel Sambuc 	}
92433d6423SLionel Sambuc 
93433d6423SLionel Sambuc 	/* prepare message */
94433d6423SLionel Sambuc 	msg.m_type       = USB_RQ_CANCEL_URB;
95433d6423SLionel Sambuc 	msg.USB_URB_ID = urb->urb_id;
96433d6423SLionel Sambuc 
97433d6423SLionel Sambuc 	/* send message */
98433d6423SLionel Sambuc 	res = ipc_sendrec(hcd_ep, &msg);
99433d6423SLionel Sambuc 
100433d6423SLionel Sambuc 	if (res != 0) {
101433d6423SLionel Sambuc 		panic("usb_cancle_urb: could not talk to hcd: %d", res);
102433d6423SLionel Sambuc 	}
103433d6423SLionel Sambuc 
104433d6423SLionel Sambuc 	if (msg.m_type != USB_REPLY) {
105433d6423SLionel Sambuc 		panic("usb_cancle_urb: got illegal response from hcd: %d", msg.m_type);
106433d6423SLionel Sambuc 	}
107433d6423SLionel Sambuc 
108433d6423SLionel Sambuc 	if (msg.USB_RESULT != 0) {
109433d6423SLionel Sambuc 		panic("usb_cancle_urb: got illegal response from hcd: %d", msg.m_type);
110433d6423SLionel Sambuc 	}
111433d6423SLionel Sambuc 
112433d6423SLionel Sambuc 	res = msg.USB_RESULT;
113433d6423SLionel Sambuc 
114433d6423SLionel Sambuc 	/* done. */
115433d6423SLionel Sambuc 	return res;
116433d6423SLionel Sambuc }
117433d6423SLionel Sambuc 
118433d6423SLionel Sambuc /*****************************************************************************
119433d6423SLionel Sambuc  *         usb_init                                                          *
120433d6423SLionel Sambuc  ****************************************************************************/
usb_init(char * name)121433d6423SLionel Sambuc int usb_init(char *name)
122433d6423SLionel Sambuc {
123433d6423SLionel Sambuc 	int res;
124433d6423SLionel Sambuc 	message msg;
125433d6423SLionel Sambuc 
126433d6423SLionel Sambuc 	/* get the endpoint of the HCD */
127433d6423SLionel Sambuc 	res = ds_retrieve_label_endpt("usbd", &hcd_ep);
128433d6423SLionel Sambuc 
129433d6423SLionel Sambuc 	if (res != 0) {
130433d6423SLionel Sambuc 		panic("usb_init: ds_retrieve_label_endpt failed for 'usb': %d", res);
131433d6423SLionel Sambuc 	}
132433d6423SLionel Sambuc 
133433d6423SLionel Sambuc 	msg.m_type = USB_RQ_INIT;
134433d6423SLionel Sambuc 
135433d6423SLionel Sambuc 	strncpy(msg.USB_RB_INIT_NAME, name, M_PATH_STRING_MAX);
136433d6423SLionel Sambuc 
137433d6423SLionel Sambuc 	res = ipc_sendrec(hcd_ep, &msg);
138433d6423SLionel Sambuc 
139433d6423SLionel Sambuc 	if (res != 0) {
140433d6423SLionel Sambuc 		panic("usb_init: can't talk to USB: %d", res);
141433d6423SLionel Sambuc 	}
142433d6423SLionel Sambuc 
143433d6423SLionel Sambuc 	if (msg.m_type != USB_REPLY) {
144433d6423SLionel Sambuc 		panic("usb_init: bad reply from USB: %d", msg.m_type);
145433d6423SLionel Sambuc 	}
146433d6423SLionel Sambuc 
147433d6423SLionel Sambuc 	if (msg.USB_RESULT != 0 ) {
148433d6423SLionel Sambuc 		panic("usb_init: init failed: %ld", msg.USB_RESULT);
149433d6423SLionel Sambuc 	}
150433d6423SLionel Sambuc 
151433d6423SLionel Sambuc 	return 0;
152433d6423SLionel Sambuc }
153433d6423SLionel Sambuc 
154433d6423SLionel Sambuc /*****************************************************************************
155433d6423SLionel Sambuc  *      _usb_urb_complete                                                    *
156433d6423SLionel Sambuc  ****************************************************************************/
_usb_urb_complete(struct usb_driver * ud,unsigned int urb_id)157*65f76edbSDavid van Moolenbroek static void _usb_urb_complete(struct usb_driver *ud, unsigned int urb_id)
158433d6423SLionel Sambuc {
159433d6423SLionel Sambuc 	/* find the corresponding URB in the urb_pending list. */
160433d6423SLionel Sambuc 	struct usb_urb * urb = NULL;
161433d6423SLionel Sambuc 	if (pending_urbs != NULL) {
162433d6423SLionel Sambuc 		if (pending_urbs->urb_id == urb_id) {
163433d6423SLionel Sambuc 			urb = pending_urbs;
164433d6423SLionel Sambuc 			pending_urbs = urb->next;
165433d6423SLionel Sambuc 		} else {
166433d6423SLionel Sambuc 			struct usb_urb *u = pending_urbs;
167433d6423SLionel Sambuc 			while (u->next) {
168433d6423SLionel Sambuc 				if (u->next->urb_id == urb_id) {
169433d6423SLionel Sambuc 					urb       = u->next;
170433d6423SLionel Sambuc 					u->next   = u->next->next;
171433d6423SLionel Sambuc 					urb->next = NULL;
172433d6423SLionel Sambuc 					break;
173433d6423SLionel Sambuc 				}
174433d6423SLionel Sambuc 				u = u->next;
175433d6423SLionel Sambuc 			}
176433d6423SLionel Sambuc 		}
177433d6423SLionel Sambuc 	}
178433d6423SLionel Sambuc 
179433d6423SLionel Sambuc 	/* Did we find a URB? */
180433d6423SLionel Sambuc 	if (urb != NULL) {
181433d6423SLionel Sambuc 		/* revoke grant */
182433d6423SLionel Sambuc 		cpf_revoke(urb->gid);
183433d6423SLionel Sambuc 		/* call completion handler */
184433d6423SLionel Sambuc #if 0
185433d6423SLionel Sambuc 		dump_urb(urb);
186433d6423SLionel Sambuc #endif
187433d6423SLionel Sambuc 		ud->urb_completion(urb);
188433d6423SLionel Sambuc 	} else {
189*65f76edbSDavid van Moolenbroek 		printf("WARN: _usb_urb_complete: did not find URB with ID %u",
190*65f76edbSDavid van Moolenbroek 		    urb_id);
191433d6423SLionel Sambuc 	}
192433d6423SLionel Sambuc }
193433d6423SLionel Sambuc 
194433d6423SLionel Sambuc /*****************************************************************************
195433d6423SLionel Sambuc  *         usb_handle_msg                                                    *
196433d6423SLionel Sambuc  ****************************************************************************/
usb_handle_msg(struct usb_driver * ud,message * msg)197433d6423SLionel Sambuc int usb_handle_msg(struct usb_driver *ud, message *msg)
198433d6423SLionel Sambuc {
199433d6423SLionel Sambuc 	/*
200433d6423SLionel Sambuc 	 * we expect kind of messages:
201433d6423SLionel Sambuc 	 *  - new usb device
202433d6423SLionel Sambuc 	 *  - removed device
203433d6423SLionel Sambuc 	 *  - URB completed
204433d6423SLionel Sambuc 	 *
205433d6423SLionel Sambuc 	 * NOTE: the hcd driver doesn't expect replies for these messages.
206433d6423SLionel Sambuc 	 */
207433d6423SLionel Sambuc 
208433d6423SLionel Sambuc 	if (!ud) {
209433d6423SLionel Sambuc 		return -1;
210433d6423SLionel Sambuc 	}
211433d6423SLionel Sambuc 
212433d6423SLionel Sambuc 	switch(msg->m_type) {
213433d6423SLionel Sambuc 		case USB_COMPLETE_URB:
214*65f76edbSDavid van Moolenbroek 			_usb_urb_complete(ud, (unsigned int)msg->USB_URB_ID);
215433d6423SLionel Sambuc 			return 0;
216433d6423SLionel Sambuc 		case USB_ANNOUCE_DEV:
217433d6423SLionel Sambuc 			ud->connect_device(msg->USB_DEV_ID, msg->USB_INTERFACES);
218433d6423SLionel Sambuc 			return 0;
219433d6423SLionel Sambuc 		case USB_WITHDRAW_DEV:
220433d6423SLionel Sambuc 			ud->disconnect_device(msg->USB_DEV_ID);
221433d6423SLionel Sambuc 			return 0;
222433d6423SLionel Sambuc 		default:
223433d6423SLionel Sambuc 			panic("usb_handle_msg: bogus message from USB");
224433d6423SLionel Sambuc 	}
225433d6423SLionel Sambuc }
226433d6423SLionel Sambuc 
2272d64210cSWojciech Zajac 
2282d64210cSWojciech Zajac /*****************************************************************************
2292d64210cSWojciech Zajac  *         usb_send_info                                                     *
2302d64210cSWojciech Zajac  *****************************************************************************/
2312d64210cSWojciech Zajac int
usb_send_info(long info_type,long info_value)2322d64210cSWojciech Zajac usb_send_info(long info_type, long info_value)
2332d64210cSWojciech Zajac {
2342d64210cSWojciech Zajac 	int res;
2352d64210cSWojciech Zajac 	message msg;
2362d64210cSWojciech Zajac 
2372d64210cSWojciech Zajac 	/* Prepare message */
2382d64210cSWojciech Zajac 	msg.m_type		= USB_RQ_SEND_INFO;
2392d64210cSWojciech Zajac 	msg.USB_INFO_TYPE	= info_type;
2402d64210cSWojciech Zajac 	msg.USB_INFO_VALUE	= info_value;
2412d64210cSWojciech Zajac 
2422d64210cSWojciech Zajac 	/* Send/receive message */
2432d64210cSWojciech Zajac 	res = ipc_sendrec(hcd_ep, &msg);
2442d64210cSWojciech Zajac 
2452d64210cSWojciech Zajac 	if (res != 0)
2462d64210cSWojciech Zajac 		panic("usb_send_info: could not talk to HCD: %d", res);
2472d64210cSWojciech Zajac 
2482d64210cSWojciech Zajac 	if (msg.m_type != USB_REPLY)
2492d64210cSWojciech Zajac 		panic("usb_send_info: got illegal response from HCD: %d", msg.m_type);
2502d64210cSWojciech Zajac 
2512d64210cSWojciech Zajac 	if (msg.USB_RESULT != 0)
2522d64210cSWojciech Zajac 		panic("usb_send_info: got illegal response from HCD: %d", msg.m_type);
2532d64210cSWojciech Zajac 
2542d64210cSWojciech Zajac 	return msg.USB_RESULT;
2552d64210cSWojciech Zajac }
256