blob: fc1d8ef6b0e21a0f7cb2d1a45a0250dc43f84965 [file] [log] [blame]
Linus Walleijeb8c6fe2006-02-03 09:46:22 +00001/*
2 * mtp-utils.c
3 *
4 * Created by Richard Low on 24/12/2005.
5 *
6 * This file adds some utils (many copied from ptpcam.c from libptp2) to
7 * use MTP devices. Include mtp-utils.h to use any of the ptp/mtp functions.
8 *
9 */
10#include "ptp.h"
11#include <errno.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <getopt.h>
16#include <unistd.h>
17#include <sys/types.h>
18#include <utime.h>
19#include <sys/stat.h>
20#include <fcntl.h>
21#include <sys/mman.h>
22#include <usb.h>
23
24#include "mtp-utils.h"
25#include "ptp-pack.h"
26
27/* OUR APPLICATION USB URB (2MB) ;) */
28#define PTPCAM_USB_URB 2097152
29
30/* this must not be too short - the original 4000 was not long
31 enough for big file transfers. I imagine the player spends a
32 bit of time gearing up to receiving lots of data. This also makes
33 connecting/disconnecting more reliable */
34#define USB_TIMEOUT 10000
35#define USB_CAPTURE_TIMEOUT 20000
36
37/* USB control message data phase direction */
38#ifndef USB_DP_HTD
39#define USB_DP_HTD (0x00 << 7) /* host to device */
40#endif
41#ifndef USB_DP_DTH
42#define USB_DP_DTH (0x01 << 7) /* device to host */
43#endif
44
45/* USB Feature selector HALT */
46#ifndef USB_FEATURE_HALT
47#define USB_FEATURE_HALT 0x00
48#endif
49
50int ptpcam_usb_timeout = USB_TIMEOUT;
51
52void close_usb(PTP_USB* ptp_usb, uint8_t interfaceNumber);
53struct usb_device* find_device (int busn, int devicen, short force);
54void find_endpoints(struct usb_device *dev, int* inep, int* outep, int* intep);
55void clear_stall(PTP_USB* ptp_usb);
56void init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev);
57static short ptp_write_func (unsigned char *bytes, unsigned int size, void *data);
58static short ptp_read_func (unsigned char *bytes, unsigned int size, void *data);
59static short ptp_check_int (unsigned char *bytes, unsigned int size, void *data);
60int usb_clear_stall_feature(PTP_USB* ptp_usb, int ep);
61int usb_get_endpoint_status(PTP_USB* ptp_usb, int ep, uint16_t* status);
62
63static short ptp_read_func (unsigned char *bytes, unsigned int size, void *data)
64{
65 int result=-1;
66 PTP_USB *ptp_usb=(PTP_USB *)data;
67 int toread=0;
68 signed long int rbytes=size;
69
70 do {
71 bytes+=toread;
72 if (rbytes>PTPCAM_USB_URB)
73 toread = PTPCAM_USB_URB;
74 else
75 toread = rbytes;
76 result=USB_BULK_READ(ptp_usb->handle, ptp_usb->inep,(char *)bytes, toread,ptpcam_usb_timeout);
77 /* sometimes retry might help */
78 if (result==0)
79 result=USB_BULK_READ(ptp_usb->handle, ptp_usb->inep,(char *)bytes, toread,ptpcam_usb_timeout);
80 if (result < 0)
81 break;
82 rbytes-=PTPCAM_USB_URB;
83 } while (rbytes>0);
84
85 if (result >= 0) {
86 return (PTP_RC_OK);
87 }
88 else
89 {
90 return PTP_ERROR_IO;
91 }
92}
93
94static short ptp_write_func (unsigned char *bytes, unsigned int size, void *data)
95{
96 int result;
97 PTP_USB *ptp_usb=(PTP_USB *)data;
98
99
100 /* only print if size < something */
101 /*int i = 0;
102 if (size < 0xff)
103 {
104 printf("-------------------------\n");
105 printf("Sending data size %d\n", size);
106 for (i = 0; i < size; i += 8)
107 {
108 int j = i;
109 for (; j<size && j<i+8; j++)
110 printf("0x%02x ", bytes[j]);
111 printf("\n");
112 }
113 printf("-------------------------\n");
114 }
115 */
116
117 result=USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,(char *)bytes,size,ptpcam_usb_timeout);
118 if (result >= 0)
119 return (PTP_RC_OK);
120 else
121 {
122 return PTP_ERROR_IO;
123 }
124}
125
126static short ptp_check_int (unsigned char *bytes, unsigned int size, void *data)
127{
128 int result;
129 PTP_USB *ptp_usb=(PTP_USB *)data;
130
131 result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)bytes,size,ptpcam_usb_timeout);
132 if (result==0)
133 result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) bytes, size, ptpcam_usb_timeout);
134 if (result >= 0) {
135 return (PTP_RC_OK);
136 } else {
137 return PTP_ERROR_IO;
138 }
139}
140
141void init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev)
142{
143 usb_dev_handle *device_handle;
144
145 params->write_func=ptp_write_func;
146 params->read_func=ptp_read_func;
147 params->check_int_func=ptp_check_int;
148 params->check_int_fast_func=ptp_check_int;
149 params->error_func=NULL;
150 params->debug_func=NULL;
151 params->sendreq_func=ptp_usb_sendreq;
152 params->senddata_func=ptp_usb_senddata;
153 params->getresp_func=ptp_usb_getresp;
154 params->getdata_func=ptp_usb_getdata;
155 params->data=ptp_usb;
156 params->transaction_id=0;
157 params->byteorder = PTP_DL_LE;
158
159 if ((device_handle=usb_open(dev))){
160 if (!device_handle) {
161 perror("usb_open()");
162 exit(0);
163 }
164 ptp_usb->handle=device_handle;
165 usb_claim_interface(device_handle,
166 dev->config->interface->altsetting->bInterfaceNumber);
167 }
168}
169
170void clear_stall(PTP_USB* ptp_usb)
171{
172 uint16_t status=0;
173 int ret;
174
175 /* check the inep status */
176 ret=usb_get_endpoint_status(ptp_usb,ptp_usb->inep,&status);
177 if (ret<0) perror ("inep: usb_get_endpoint_status()");
178 /* and clear the HALT condition if happend */
179 else if (status) {
180 printf("Resetting input pipe!\n");
181 ret=usb_clear_stall_feature(ptp_usb,ptp_usb->inep);
182 /*usb_clear_halt(ptp_usb->handle,ptp_usb->inep); */
183 if (ret<0)perror ("usb_clear_stall_feature()");
184 }
185 status=0;
186
187 /* check the outep status */
188 ret=usb_get_endpoint_status(ptp_usb,ptp_usb->outep,&status);
189 if (ret<0) perror ("outep: usb_get_endpoint_status()");
190 /* and clear the HALT condition if happend */
191 else if (status) {
192 printf("Resetting output pipe!\n");
193 ret=usb_clear_stall_feature(ptp_usb,ptp_usb->outep);
194 /*usb_clear_halt(ptp_usb->handle,ptp_usb->outep); */
195 if (ret<0)perror ("usb_clear_stall_feature()");
196 }
197
198 /*usb_clear_halt(ptp_usb->handle,ptp_usb->intep); */
199}
200
201void close_usb(PTP_USB* ptp_usb, uint8_t interfaceNumber)
202{
203 clear_stall(ptp_usb);
204 usb_release_interface(ptp_usb->handle, interfaceNumber);
205 usb_close(ptp_usb->handle);
206}
207
208
209struct usb_bus*
210init_usb()
211{
212 usb_init();
213 usb_find_busses();
214 usb_find_devices();
215 return (usb_get_busses());
216}
217
218/*
219 find_device() returns the pointer to a usb_device structure matching
220 given busn, devicen numbers. If any or both of arguments are 0 then the
221 first matching PTP device structure is returned.
222 */
223struct usb_device* find_device (int busn, int devn, short force)
224{
225 struct usb_bus *bus;
226 struct usb_device *dev;
227
228 bus=init_usb();
229 for (; bus; bus = bus->next)
230 for (dev = bus->devices; dev; dev = dev->next)
231 /* somtimes dev->config is null, not sure why... */
232 if (dev->config != NULL)
233 if (dev->descriptor.bDeviceClass!=USB_CLASS_HUB)
234 {
235 int curbusn, curdevn;
236
237 curbusn=strtol(bus->dirname,NULL,10);
238 curdevn=strtol(dev->filename,NULL,10);
239
240 if (devn==0) {
241 if (busn==0) return dev;
242 if (curbusn==busn) return dev;
243 } else {
244 if ((busn==0)&&(curdevn==devn)) return dev;
245 if ((curbusn==busn)&&(curdevn==devn)) return dev;
246 }
247 }
248 return NULL;
249}
250
251/* this is a temporary function to connect to the first device we can, that has vendor ID CREATIVE_VENDOR_ID */
252
253uint16_t connect_first_device(PTPParams *params, PTP_USB *ptp_usb, uint8_t *interfaceNumber)
254{
255 struct usb_bus *bus;
256 struct usb_device *dev;
257
258 bus=init_usb();
259 for (; bus; bus = bus->next)
260 {
261 for (dev = bus->devices; dev; dev = dev->next)
262 {
263 if (dev->descriptor.bDeviceClass!=USB_CLASS_HUB && dev->descriptor.idVendor==CREATIVE_VENDOR_ID)
264 {
265 uint16_t ret=0;
266 int n;
267 struct usb_endpoint_descriptor *ep;
268 PTPDeviceInfo deviceinfo;
269
270 ep = dev->config->interface->altsetting->endpoint;
271 n=dev->config->interface->altsetting->bNumEndpoints;
272
273 find_endpoints(dev,&(ptp_usb->inep),&(ptp_usb->outep),&(ptp_usb->intep));
274 init_ptp_usb(params, ptp_usb, dev);
275
276 ret = ptp_opensession(params,1);
277 if (ret != PTP_RC_OK)
278 {
279 printf("Could not open session!\n Try to reset the camera.\n");
280 usb_release_interface(ptp_usb->handle,dev->config->interface->altsetting->bInterfaceNumber);
281 continue;
282 }
283
284 ret = ptp_getdeviceinfo(params, &deviceinfo);
285 if (ret != PTP_RC_OK)
286 {
287 printf("Could not get device info!\n");
288 usb_release_interface(ptp_usb->handle,dev->config->interface->altsetting->bInterfaceNumber);
289 return PTP_CD_RC_ERROR_CONNECTING;
290 }
291
292 /* we're connected, return ok */
293 *interfaceNumber = dev->config->interface->altsetting->bInterfaceNumber;
294
295 return PTP_CD_RC_CONNECTED;
296 }
297 }
298 }
299 /* none found */
300 return PTP_CD_RC_NO_DEVICES;
301}
302
303void find_endpoints(struct usb_device *dev, int* inep, int* outep, int* intep)
304{
305 int i,n;
306 struct usb_endpoint_descriptor *ep;
307
308 ep = dev->config->interface->altsetting->endpoint;
309 n=dev->config->interface->altsetting->bNumEndpoints;
310
311 for (i=0;i<n;i++) {
312 if (ep[i].bmAttributes==USB_ENDPOINT_TYPE_BULK) {
313 if ((ep[i].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==
314 USB_ENDPOINT_DIR_MASK)
315 {
316 *inep=ep[i].bEndpointAddress;
317 }
318 if ((ep[i].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==0)
319 {
320 *outep=ep[i].bEndpointAddress;
321 }
322 } else if (ep[i].bmAttributes==USB_ENDPOINT_TYPE_INTERRUPT){
323 if ((ep[i].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==
324 USB_ENDPOINT_DIR_MASK)
325 {
326 *intep=ep[i].bEndpointAddress;
327 }
328 }
329 }
330}
331
332
333int open_device (int busn, int devn, short force, PTP_USB *ptp_usb, PTPParams *params, struct usb_device **dev)
334{
335#ifdef DEBUG
336 printf("dev %i\tbus %i\n",devn,busn);
337#endif
338
339 *dev=find_device(busn,devn,force);
340 if (*dev==NULL) {
341 fprintf(stderr,"could not find any device matching given "
342 "bus/dev numbers\n");
343 exit(-1);
344 }
345 find_endpoints(*dev,&ptp_usb->inep,&ptp_usb->outep,&ptp_usb->intep);
346
347 init_ptp_usb(params, ptp_usb, *dev);
348 if (ptp_opensession(params,1)!=PTP_RC_OK) {
349 fprintf(stderr,"ERROR: Could not open session!\n");
350 close_usb(ptp_usb, (*dev)->config->interface->altsetting->bInterfaceNumber);
351 return -1;
352 }
353 return 0;
354}
355
356void close_device (PTP_USB *ptp_usb, PTPParams *params, uint8_t interfaceNumber)
357{
358 if (ptp_closesession(params)!=PTP_RC_OK)
359 fprintf(stderr,"ERROR: Could not close session!\n");
360 close_usb(ptp_usb, interfaceNumber);
361}
362
363int usb_clear_stall_feature(PTP_USB* ptp_usb, int ep)
364{
365
366 return (usb_control_msg(ptp_usb->handle,
367 USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE, USB_FEATURE_HALT,
368 ep, NULL, 0, 3000));
369}
370
371int usb_get_endpoint_status(PTP_USB* ptp_usb, int ep, uint16_t* status)
372{
373 return (usb_control_msg(ptp_usb->handle,
374 USB_DP_DTH|USB_RECIP_ENDPOINT, USB_REQ_GET_STATUS,
375 USB_FEATURE_HALT, ep, (char *)status, 2, 3000));
376}
377
378uint16_t send_file(PTPParams* params, const char* filename, const char* friendlyfilename, uint16_t objectFormat, Progress_Callback* callback, uint32_t* handle)
379{
380 int file;
381 uint16_t ret;
382 struct stat sb;
383 uint32_t store = 0;
384 uint32_t parenthandle = 0;
385 PTPUSBBulkContainerSend usbdata;
386 unsigned int remain;
387 int done = 0;
388 PTPContainer ptp;
389
390 PTPObjectInfo newInfo;
391 uint32_t filesize = 0;
392 unsigned char *data = NULL;
393
394 // check file exists
395 if (stat(filename, &sb) == -1)
396 {
397 printf("could not stat file\n");
398 return 0;
399 }
400 filesize = sb.st_size;
401
402 file=open(filename, O_RDONLY);
403
404 if (friendlyfilename != NULL)
405 newInfo.Filename = (char *)friendlyfilename;
406 else
407 newInfo.Filename = (char *)filename;
408 newInfo.ObjectFormat = objectFormat;
409 newInfo.ObjectCompressedSize = filesize;
410
411 ret = ptp_sendobjectinfo(params, &store, &parenthandle, handle, &newInfo);
412 if (ret != PTP_RC_OK)
413 {
414 ptp_perror(params, ret);
415 close(file);
416 printf("Could not send object info\n");
417 return ret;
418 }
419
420 memset(&ptp,0,sizeof(ptp));
421 ptp.Code=PTP_OC_SendObject;
422 ptp.Nparam=0;
423 ptp.Transaction_ID=params->transaction_id++;
424 ptp.SessionID=params->session_id;
425
426 ret = params->sendreq_func(params, &ptp);
427 if (ret != PTP_RC_OK)
428 {
429 ptp_perror(params, ret);
430 close(file);
431 printf("Could not send request\n");
432 return ret;
433 }
434
435 /* build appropriate USB container */
436 usbdata.length=htod32(sizeof(usbdata)+filesize);
437 usbdata.type=htod16(PTP_USB_CONTAINER_DATA);
438 usbdata.code=htod16(PTP_OC_SendObject);
439 usbdata.trans_id=htod32(ptp.Transaction_ID);
440
441 ret=params->write_func((unsigned char *)&usbdata, sizeof(usbdata), params->data);
442 if (ret!=PTP_RC_OK) {
443 ret = PTP_ERROR_IO;
444 ptp_perror(params, ret);
445 return ret;
446 }
447
448 /* This space will be used as a reading ring buffer for the transfers */
449 data = (unsigned char *)malloc(BLOCK_SIZE);
450
451 remain = filesize;
452 while (done == 0)
453 {
454 int readsize = remain>BLOCK_SIZE?BLOCK_SIZE:remain;
455 int bytesdone = filesize-remain;
456
457 read(file, data, readsize);
458
459 if (callback != NULL)
460 {
461 if (bytesdone % CALLBACK_SIZE == 0)
462 callback(bytesdone, filesize);
463 }
464 ret=params->write_func(data, readsize, params->data);
465 if (ret!=PTP_RC_OK) {
466 ret = PTP_ERROR_IO;
467 break;
468 }
469 if (remain <= BLOCK_SIZE)
470 done = 1;
471 else
472 {
473 remain -= BLOCK_SIZE;
474 }
475 }
476
477 if (done != 0 && callback != NULL)
478 callback(filesize, filesize);
479
480 /* write zero to end for some reason... but only sometimes!! */
481 if (done != 0 && filesize % MTP_DEVICE_BUF_SIZE == 0)
482 {
483 ret=params->write_func(data, 0, params->data);
484 }
485
486 /* get response */
487 if (ret!=PTP_ERROR_IO && params->getresp_func(params, &ptp) != PTP_RC_OK)
488 ret = PTP_ERROR_IO;
489
490 free(data);
491
492 close(file);
493 return ret;
494}
495
496int get_file(PTPParams* params, uint32_t handle, const char* filename, Progress_Callback* callback)
497{
498 int file;
499 PTPObjectInfo oi;
500 char *image;
501 int ret;
502 extern Progress_Callback* globalCallback;
503
504 if (ptp_getobjectinfo(params,handle, &oi) != PTP_RC_OK)
505 {
506 printf("Could not get object info\n");
507 return -1;
508 }
509 if (oi.ObjectFormat == PTP_OFC_Association)
510 return -1;
511
512 file=open(filename, O_EXCL|O_RDWR|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP);
513 if (file==-1) {
514 if (errno==EEXIST) {
515 printf("Skipping file: \"%s\", file exists!\n",filename);
516 return -1;
517 }
518 return -1;
519 }
520 lseek(file,oi.ObjectCompressedSize-1,SEEK_SET);
521 write(file,"",1);
522 if (file<0) return -1;
523 image=mmap(0,oi.ObjectCompressedSize,PROT_READ|PROT_WRITE,MAP_SHARED,file,0);
524 if (image==MAP_FAILED) {
525 close(file);
526 return -1;
527 }
528 fflush(NULL);
529
530 globalCallback=callback;
531 ret=ptp_getobject(params,handle,&image);
532 globalCallback=NULL;
533
534 munmap(image,oi.ObjectCompressedSize);
535 close(file);
536 /*timebuf.actime=oi.ModificationDate;
537 timebuf.modtime=oi.CaptureDate;
538 utime(filename,&timebuf);*/
539 if (ret!=PTP_RC_OK) {
540 printf ("error!\n");
541 }
542 // todo: error return code
543 return 0;
544
545}