Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6 | f9daq | 1 | /************************************************************************\ |
2 | ## ## |
||
3 | ## Creation Date: 17 Mar 2007 ## |
||
4 | ## Last Update: 18 Mar 2007 ## |
||
5 | ## Author: XInstruments ## |
||
6 | ## ## |
||
7 | \************************************************************************/ |
||
8 | |||
9 | |||
10 | #include <linux/kernel.h> |
||
11 | #include <linux/init.h> |
||
12 | #include <linux/module.h> |
||
13 | #include <linux/usb.h> |
||
14 | #include <asm/uaccess.h> |
||
15 | #include "usmcdrv-driver.h" |
||
16 | #include "usmctypes.h" |
||
17 | #include "usmcpkt.h" |
||
18 | |||
19 | #include <linux/slab.h> |
||
20 | |||
21 | #if HAVE_UNLOCKED_IOCTL |
||
22 | #include <linux/mutex.h> |
||
23 | #else |
||
24 | #include <linux/smp_lock.h> |
||
25 | #endif |
||
26 | #define info( format, arg...) printk(KERN_INFO USMC_DEV_NAME ": " format "\n", ## arg) |
||
27 | #define err( format, arg...) printk(KERN_ERR USMC_DEV_NAME ": " format "\n", ## arg) |
||
28 | |||
29 | |||
30 | |||
31 | MODULE_DESCRIPTION ( "MicroSMC driver for Linux" ); |
||
32 | MODULE_AUTHOR ( "XInstruments" ); |
||
33 | MODULE_LICENSE ( "GPL" ); |
||
34 | MODULE_VERSION ( "1.0" ); |
||
35 | |||
36 | |||
37 | static int num_devices = 0; // Number of currently connected devices. |
||
38 | |||
39 | |||
40 | /*----------------------------------------------------- |
||
41 | Init/Deinit section |
||
42 | -----------------------------------------------------*/ |
||
43 | static int usmcdrv_init_module ( void ) |
||
44 | { |
||
45 | int res; |
||
46 | |||
47 | printk ( KERN_DEBUG "Module usmcdrv init\n" ); |
||
48 | res = usb_register ( &usmc_driver ); |
||
49 | |||
50 | if ( res ) |
||
51 | err ( "usb_register failed. Error number %d", res ); |
||
52 | |||
53 | return res; |
||
54 | } |
||
55 | |||
56 | |||
57 | static void usmcdrv_exit_module ( void ) |
||
58 | { |
||
59 | printk ( KERN_DEBUG "Module usmcdrv exit\n" ); |
||
60 | |||
61 | usb_deregister ( &usmc_driver ); |
||
62 | } |
||
63 | |||
64 | |||
65 | module_init ( usmcdrv_init_module ); |
||
66 | module_exit ( usmcdrv_exit_module ); |
||
67 | |||
68 | |||
69 | /*----------------------------------------------------- |
||
70 | Connect/Disconnect section |
||
71 | -----------------------------------------------------*/ |
||
72 | static int usmc_probe ( struct usb_interface * interface, |
||
73 | const struct usb_device_id * id ) |
||
74 | { |
||
75 | struct usb_usmc * dev = NULL; |
||
76 | int retval = -ENOMEM; |
||
77 | |||
78 | dev = kzalloc ( sizeof ( *dev ), GFP_KERNEL ); |
||
79 | |||
80 | if ( dev == NULL ) { |
||
81 | err ( "Out of memory" ); |
||
82 | goto error; |
||
83 | } |
||
84 | |||
85 | kref_init ( &dev -> kref ); |
||
86 | sema_init ( &dev -> limit_sem, WRITES_IN_FLIGHT ); |
||
87 | |||
88 | dev -> udev = usb_get_dev ( interface_to_usbdev ( interface ) ); |
||
89 | dev -> interface = interface; |
||
90 | |||
91 | usb_set_intfdata ( interface, dev ); |
||
92 | retval = usb_register_dev ( interface, &usmc_class ); |
||
93 | |||
94 | if ( retval ) |
||
95 | { |
||
96 | err ( "Not able to get a minor for this device." ); |
||
97 | usb_set_intfdata ( interface, NULL ); |
||
98 | goto error; |
||
99 | } |
||
100 | |||
101 | info ( USMC_DEV_NAME " device now attached to USBusmc-%d", interface -> minor ); |
||
102 | num_devices++; |
||
103 | |||
104 | return 0; |
||
105 | error: |
||
106 | if ( dev ) |
||
107 | kref_put ( &dev -> kref, usmc_delete ); |
||
108 | |||
109 | return retval; |
||
110 | } |
||
111 | |||
112 | |||
113 | static void usmc_disconnect ( struct usb_interface * interface ) |
||
114 | { |
||
115 | struct usb_usmc * dev; |
||
116 | int minor = interface -> minor; |
||
117 | |||
118 | #if HAVE_UNLOCKED_IOCTL |
||
119 | struct mutex fs_mutex; |
||
120 | mutex_init(&fs_mutex); |
||
121 | mutex_lock(&fs_mutex); |
||
122 | #else |
||
123 | lock_kernel(); |
||
124 | #endif |
||
125 | |||
126 | dev = usb_get_intfdata ( interface ); |
||
127 | usb_set_intfdata ( interface, NULL ); |
||
128 | |||
129 | /* give back our minor */ |
||
130 | usb_deregister_dev ( interface, &usmc_class ); |
||
131 | num_devices--; |
||
132 | |||
133 | #if HAVE_UNLOCKED_IOCTL |
||
134 | mutex_unlock(&fs_mutex); |
||
135 | #else |
||
136 | unlock_kernel(); |
||
137 | #endif |
||
138 | |||
139 | /* decrement our usage count */ |
||
140 | kref_put ( &dev -> kref, usmc_delete ); |
||
141 | |||
142 | info ( USMC_DEV_NAME " #%d now disconnected", minor ); |
||
143 | } |
||
144 | |||
145 | |||
146 | /*----------------------------------------------------- |
||
147 | File operations section |
||
148 | -----------------------------------------------------*/ |
||
149 | static int usmc_open ( struct inode * inode, struct file * file ) |
||
150 | { |
||
151 | struct usb_usmc * dev; |
||
152 | struct usb_interface * interface; |
||
153 | int subminor; |
||
154 | int retval = 0; |
||
155 | |||
156 | info ( "usmc_open" ); |
||
157 | |||
158 | subminor = iminor ( inode ); |
||
159 | |||
160 | interface = usb_find_interface ( &usmc_driver, subminor ); |
||
161 | |||
162 | if ( !interface ) |
||
163 | { |
||
164 | err ( "%s - error, can't find device for minor %d", |
||
165 | __FUNCTION__, subminor ); |
||
166 | retval = -ENODEV; |
||
167 | |||
168 | goto exit; |
||
169 | } |
||
170 | |||
171 | dev = usb_get_intfdata ( interface ); |
||
172 | |||
173 | if ( !dev ) |
||
174 | { |
||
175 | retval = -ENODEV; |
||
176 | goto exit; |
||
177 | } |
||
178 | |||
179 | /* increment our usage count for the device */ |
||
180 | kref_get ( &dev -> kref ); |
||
181 | |||
182 | /* save our object in the file's private structure */ |
||
183 | file -> private_data = dev; |
||
184 | exit: |
||
185 | return retval; |
||
186 | } |
||
187 | |||
188 | |||
189 | static int usmc_release ( struct inode * inode, struct file * file ) |
||
190 | { |
||
191 | struct usb_usmc * dev; |
||
192 | |||
193 | info ( "usmc_release" ); |
||
194 | |||
195 | dev = ( struct usb_usmc * ) file -> private_data; |
||
196 | |||
197 | if ( dev == NULL ) |
||
198 | return -ENODEV; |
||
199 | |||
200 | /* decrement the count on our device */ |
||
201 | kref_put ( &dev -> kref, usmc_delete ); |
||
202 | |||
203 | return 0; |
||
204 | } |
||
205 | |||
206 | |||
207 | static long usmc_ioctl ( struct file * file, unsigned int ioctl_num, unsigned long ioctl_param ) |
||
208 | { |
||
209 | struct usb_usmc * dev; |
||
210 | char * user_buf; |
||
211 | char * kern_buf; |
||
212 | char * kern_data_buf; |
||
213 | __u32 dwTimeOut; |
||
214 | long dwRes; |
||
215 | int i; |
||
216 | unsigned int pipe0; |
||
217 | |||
218 | __u8 bRequestType; |
||
219 | __u8 bRequest; |
||
220 | __u16 wValue; |
||
221 | __u16 wIndex; |
||
222 | __u16 wLength; |
||
223 | |||
224 | dev = ( struct usb_usmc * ) file -> private_data; |
||
225 | user_buf = ( char * ) ioctl_param; |
||
226 | kern_buf = NULL; |
||
227 | dwTimeOut = 30000; |
||
228 | dwRes = USMC_SUCCESS; |
||
229 | |||
230 | switch ( ioctl_num ) |
||
231 | { |
||
232 | case IOCTL_GET_DESCRIPTOR_CONFIGURATION: |
||
233 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_DESCRIPTOR_CONFIGURATION" ); |
||
234 | |||
235 | bsp_GetDescriptor ( GET_DESCRIPTOR_CONFIGURATION, |
||
236 | &bRequestType, |
||
237 | &bRequest, |
||
238 | &wValue, |
||
239 | &wIndex, |
||
240 | &wLength ); |
||
241 | |||
242 | break; |
||
243 | case IOCTL_GET_DESCRIPTOR_DEVICE: |
||
244 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_DESCRIPTOR_DEVICE" ); |
||
245 | |||
246 | bsp_GetDescriptor ( IOCTL_GET_DESCRIPTOR_DEVICE, |
||
247 | &bRequestType, |
||
248 | &bRequest, |
||
249 | &wValue, |
||
250 | &wIndex, |
||
251 | &wLength ); |
||
252 | |||
253 | break; |
||
254 | case IOCTL_GET_DESCRIPTOR_STRING: |
||
255 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_DESCRIPTOR_STRING" ); |
||
256 | |||
257 | bsp_GetDescriptor ( IOCTL_GET_DESCRIPTOR_STRING, |
||
258 | &bRequestType, |
||
259 | &bRequest, |
||
260 | &wValue, |
||
261 | &wIndex, |
||
262 | &wLength ); |
||
263 | |||
264 | break; |
||
265 | case IOCTL_GET_STATUS_DEVICE: |
||
266 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_STATUS_DEVICE" ); |
||
267 | |||
268 | bsp_GetStatus ( IOCTL_GET_STATUS_DEVICE, |
||
269 | &bRequestType, |
||
270 | &bRequest, |
||
271 | &wValue, |
||
272 | &wIndex, |
||
273 | &wLength ); |
||
274 | |||
275 | break; |
||
276 | case IOCTL_GET_STATUS_ENDPOINT: |
||
277 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_STATUS_ENDPOINT" ); |
||
278 | |||
279 | bsp_GetStatus ( IOCTL_GET_STATUS_ENDPOINT, |
||
280 | &bRequestType, |
||
281 | &bRequest, |
||
282 | &wValue, |
||
283 | &wIndex, |
||
284 | &wLength ); |
||
285 | |||
286 | break; |
||
287 | case IOCTL_GET_STATUS_INTERFACE: |
||
288 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_STATUS_INTERFACE" ); |
||
289 | |||
290 | bsp_GetStatus ( IOCTL_GET_STATUS_INTERFACE, |
||
291 | &bRequestType, |
||
292 | &bRequest, |
||
293 | &wValue, |
||
294 | &wIndex, |
||
295 | &wLength ); |
||
296 | |||
297 | break; |
||
298 | case IOCTL_GET_VERSION: |
||
299 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_VERSION" ); |
||
300 | |||
301 | bsp_GetVersion ( &bRequestType, |
||
302 | &bRequest, |
||
303 | &wValue, |
||
304 | &wIndex, |
||
305 | &wLength ); |
||
306 | |||
307 | break; |
||
308 | case IOCTL_GET_SERIAL: |
||
309 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_SERIAL" ); |
||
310 | |||
311 | bsp_GetSerial ( &bRequestType, |
||
312 | &bRequest, |
||
313 | &wValue, |
||
314 | &wIndex, |
||
315 | &wLength ); |
||
316 | |||
317 | break; |
||
318 | case IOCTL_GET_ENCODER_STATE: |
||
319 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_ENCODER_STATE" ); |
||
320 | |||
321 | bsp_GetEncoderState ( &bRequestType, |
||
322 | &bRequest, |
||
323 | &wValue, |
||
324 | &wIndex, |
||
325 | &wLength ); |
||
326 | |||
327 | break; |
||
328 | case IOCTL_GET_STATE: |
||
329 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_STATE" ); |
||
330 | |||
331 | bsp_GetState ( &bRequestType, |
||
332 | &bRequest, |
||
333 | &wValue, |
||
334 | &wIndex, |
||
335 | &wLength ); |
||
336 | |||
337 | break; |
||
338 | case IOCTL_GO_TO: |
||
339 | info ( "usmc_ioctl. ioctl_num: IOCTL_GO_TO" ); |
||
340 | |||
341 | kern_buf = bsp_GoTo ( user_buf, |
||
342 | &bRequestType, |
||
343 | &bRequest, |
||
344 | &wValue, |
||
345 | &wIndex, |
||
346 | &wLength ); |
||
347 | |||
348 | break; |
||
349 | case IOCTL_SET_MODE: |
||
350 | info ( "usmc_ioctl. ioctl_num: IOCTL_SET_MODE" ); |
||
351 | |||
352 | kern_buf = bsp_SetMode ( user_buf, |
||
353 | &bRequestType, |
||
354 | &bRequest, |
||
355 | &wValue, |
||
356 | &wIndex, |
||
357 | &wLength ); |
||
358 | |||
359 | break; |
||
360 | case IOCTL_SET_PARAMETERS: |
||
361 | info ( "usmc_ioctl. ioctl_num: IOCTL_SET_PARAMETERS" ); |
||
362 | |||
363 | kern_buf = bsp_SetParameters ( user_buf, |
||
364 | &bRequestType, |
||
365 | &bRequest, |
||
366 | &wValue, |
||
367 | &wIndex, |
||
368 | &wLength ); |
||
369 | |||
370 | break; |
||
371 | case IOCTL_DOWNLOAD: |
||
372 | info ( "usmc_ioctl. ioctl_num: IOCTL_DOWNLOAD" ); |
||
373 | |||
374 | kern_buf = bsp_Download ( user_buf, |
||
375 | &bRequestType, |
||
376 | &bRequest, |
||
377 | &wValue, |
||
378 | &wIndex, |
||
379 | &wLength ); |
||
380 | |||
381 | break; |
||
382 | case IOCTL_SET_SERIAL: |
||
383 | info ( "usmc_ioctl. ioctl_num: IOCTL_SET_SERIAL" ); |
||
384 | |||
385 | kern_buf = bsp_SetSerial ( user_buf, |
||
386 | &bRequestType, |
||
387 | &bRequest, |
||
388 | &wValue, |
||
389 | &wIndex, |
||
390 | &wLength ); |
||
391 | |||
392 | break; |
||
393 | case IOCTL_SET_CURRENT_POSITION: |
||
394 | info ( "usmc_ioctl. ioctl_num: IOCTL_SET_CURRENT_POSITION" ); |
||
395 | |||
396 | bsp_SetCurrentPosition ( user_buf, |
||
397 | &bRequestType, |
||
398 | &bRequest, |
||
399 | &wValue, |
||
400 | &wIndex, |
||
401 | &wLength ); |
||
402 | |||
403 | break; |
||
404 | case IOCTL_STOP_STEP_MOTOR: |
||
405 | info ( "usmc_ioctl. ioctl_num: IOCTL_STOP_STEP_MOTOR" ); |
||
406 | |||
407 | bsp_StopStepMotor ( &bRequestType, |
||
408 | &bRequest, |
||
409 | &wValue, |
||
410 | &wIndex, |
||
411 | &wLength ); |
||
412 | |||
413 | break; |
||
414 | case IOCTL_EMULATE_BUTTONS: |
||
415 | info ( "usmc_ioctl. ioctl_num: IOCTL_EMULATE_BUTTONS" ); |
||
416 | |||
417 | bsp_EmulateButtons ( user_buf, |
||
418 | &bRequestType, |
||
419 | &bRequest, |
||
420 | &wValue, |
||
421 | &wIndex, |
||
422 | &wLength ); |
||
423 | |||
424 | break; |
||
425 | case IOCTL_SAVE_PARAMETERS: |
||
426 | info ( "usmc_ioctl. ioctl_num: IOCTL_SAVE_PARAMETERS" ); |
||
427 | |||
428 | bsp_SaveParameters ( &bRequestType, |
||
429 | &bRequest, |
||
430 | &wValue, |
||
431 | &wIndex, |
||
432 | &wLength ); |
||
433 | |||
434 | break; |
||
435 | case IOCTL_GET_NOD: |
||
436 | info ( "usmc_ioctl. ioctl_num: IOCTL_GET_NOD" ); |
||
437 | dwRes = 1; |
||
438 | kern_buf = user_to_kernel ( user_buf, 1 ); |
||
439 | kern_buf [0] = num_devices; |
||
440 | |||
441 | break; |
||
442 | default: |
||
443 | err ( "usmc_ioctl. unknown ioctl_num: %d", ioctl_num ); |
||
444 | dwRes = -1; |
||
445 | |||
446 | break; |
||
447 | } |
||
448 | |||
449 | |||
450 | if ( dwRes == USMC_SUCCESS ) |
||
451 | { |
||
452 | if ( ( bRequestType & USB_DIR_IN ) != USB_DIR_IN ) |
||
453 | { |
||
454 | kern_data_buf = wLength > 0 ? kern_buf + 4 : NULL; |
||
455 | pipe0 = usb_sndctrlpipe ( dev -> udev, 0 ); |
||
456 | |||
457 | } else { |
||
458 | kern_buf = ( char * ) kzalloc ( ( ssize_t ) wLength, GFP_KERNEL ); |
||
459 | kern_data_buf = kern_buf; |
||
460 | pipe0 = usb_rcvctrlpipe ( dev -> udev, 0 ); |
||
461 | } |
||
462 | |||
463 | dwRes = usb_control_msg ( dev -> udev, |
||
464 | pipe0, |
||
465 | bRequest, |
||
466 | bRequestType, |
||
467 | wValue, |
||
468 | wIndex, |
||
469 | kern_data_buf, |
||
470 | wLength, |
||
471 | dwTimeOut ); |
||
472 | |||
473 | if ( ( bRequestType & USB_DIR_IN ) == USB_DIR_IN ) |
||
474 | { |
||
475 | for ( i = 0 ; i < wLength ; i++ ) |
||
476 | put_user ( kern_data_buf [i], user_buf + i ); |
||
477 | } |
||
478 | } else if ( dwRes > 0 ) { |
||
479 | for ( i = 0 ; i < dwRes ; i++ ) |
||
480 | put_user ( kern_buf [i], user_buf + i ); |
||
481 | } |
||
482 | |||
483 | |||
484 | kfree ( kern_buf ); |
||
485 | |||
486 | if ( dwRes < 0 ) { |
||
487 | err ( "usmc_ioctl failed: dwRes: %d", (int) dwRes ); |
||
488 | } else { |
||
489 | info ( "transmitted %d bytes over control pipe0", (int) dwRes ); |
||
490 | } |
||
491 | |||
492 | |||
493 | return dwRes; |
||
494 | } |
||
495 | |||
496 | |||
497 | /*----------------------------------------------------- |
||
498 | Util functions section |
||
499 | -----------------------------------------------------*/ |
||
500 | static void usmc_delete ( struct kref * kref ) |
||
501 | { |
||
502 | struct usb_usmc * dev = to_usmc_dev ( kref ); |
||
503 | |||
504 | usb_put_dev ( dev -> udev ); |
||
505 | kfree ( dev ); |
||
506 | } |