Files
libgphoto2/camlibs/ptp2/ptp.c
Mariusz Woloszyn 27efe54573 * ptp.c: cosmetic
* ptp-pack.c: fixed a bug in ptp_unpack_DI(), totallen could be bigger than uint8_t
* library.c:  added camera summary


git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@6015 67ed7778-7388-44ab-90cf-0a291f65f57c
2003-01-08 15:44:07 +00:00

785 lines
20 KiB
C

/* ptp.c
*
* Copyright (C) 2001 Mariusz Woloszyn <emsi@ipartners.pl>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include "ptp.h"
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifdef ENABLE_NLS
# include <libintl.h>
# undef _
# define _(String) dgettext (PACKAGE, String)
# ifdef gettext_noop
# define N_(String) gettext_noop (String)
# else
# define N_(String) (String)
# endif
#else
# define textdomain(String) (String)
# define gettext(String) (String)
# define dgettext(Domain,Message) (Message)
# define dcgettext(Domain,Message,Type) (Message)
# define bindtextdomain(Domain,Directory) (Domain)
# define _(String) (String)
# define N_(String) (String)
#endif
#define CHECK_PTP_RC(result) {uint16_t r=(result); if (r!=PTP_RC_OK) return r;}
#define PTP_CNT_INIT(cnt) {memset(&cnt,0,sizeof(cnt));}
static void
ptp_debug (PTPParams *params, const char *format, ...)
{
va_list args;
va_start (args, format);
if (params->debug_func)
params->debug_func (params->data, format, args);
else
vfprintf (stderr, format, args);
va_end (args);
}
static void
ptp_error (PTPParams *params, const char *format, ...)
{
va_list args;
va_start (args, format);
if (params->error_func)
params->error_func (params->data, format, args);
else
vfprintf (stderr, format, args);
va_end (args);
}
// Pack / unpack functions
#include "ptp-pack.c"
// send / receive functions
uint16_t
ptp_usb_sendreq (PTPParams* params, PTPContainer* req)
{
uint16_t ret;
PTPUSBBulkContainer usbreq;
/* build appropriate USB container */
usbreq.length=htod32(PTP_USB_BULK_REQ_LEN);
usbreq.type=htod16(PTP_USB_CONTAINER_COMMAND);
usbreq.code=htod16(req->Code);
usbreq.trans_id=htod32(req->Transaction_ID);
usbreq.payload.params.param1=htod32(req->Param1);
usbreq.payload.params.param2=htod32(req->Param2);
usbreq.payload.params.param3=htod32(req->Param3);
usbreq.payload.params.param4=htod32(req->Param4);
usbreq.payload.params.param5=htod32(req->Param5);
/* send it to responder */
ret=params->write_func((unsigned char *)&usbreq,PTP_USB_BULK_REQ_LEN,
params->data);
if (ret!=PTP_RC_OK) {
ret = PTP_ERROR_IO;
ptp_error (params,
"PTP: request code 0x%4x sending req error 0x%.2x",
req->Code,ret);
}
return ret;
}
uint16_t
ptp_usb_senddata (PTPParams* params, PTPContainer* ptp,
unsigned char *data, unsigned int size)
{
uint16_t ret;
PTPUSBBulkContainer usbdata;
/* build appropriate USB container */
usbdata.length=htod32(PTP_USB_BULK_HDR_LEN+size);
usbdata.type=htod16(PTP_USB_CONTAINER_DATA);
usbdata.code=htod16(ptp->Code);
usbdata.trans_id=htod32(ptp->Transaction_ID);
memcpy(usbdata.payload.data,data,
(size<PTP_USB_BULK_PAYLOAD_LEN)?size:PTP_USB_BULK_PAYLOAD_LEN);
/* send first part of data */
ret=params->write_func((unsigned char *)&usbdata, PTP_USB_BULK_HDR_LEN+
((size<PTP_USB_BULK_PAYLOAD_LEN)?size:PTP_USB_BULK_PAYLOAD_LEN),
params->data);
if (ret!=PTP_RC_OK) {
ret = PTP_ERROR_IO;
ptp_error (params,
"PTP: request code 0x%4x sending data error 0x%.2x",
ptp->Code,ret);
return ret;
}
if (size<=PTP_USB_BULK_PAYLOAD_LEN) return ret;
/* if everything OK send the rest */
ret=params->write_func (data+PTP_USB_BULK_PAYLOAD_LEN,
size-PTP_USB_BULK_PAYLOAD_LEN, params->data);
if (ret!=PTP_RC_OK) {
ret = PTP_ERROR_IO;
ptp_error (params,
"PTP: request code 0x%4x sending data error 0x%.2x",
ptp->Code,ret);
}
return ret;
}
uint16_t
ptp_usb_getdata (PTPParams* params, PTPContainer* ptp,
unsigned char **data)
{
uint16_t ret;
PTPUSBBulkContainer usbdata;
PTP_CNT_INIT(usbdata);
if (*data!=NULL) return PTP_ERROR_BADPARAM;
do {
unsigned int len;
/* read first(?) part of data */
ret=params->read_func((unsigned char *)&usbdata,
sizeof(usbdata), params->data);
if (ret!=PTP_RC_OK) {
ret = PTP_ERROR_IO;
break;
} else
if (dtoh16(usbdata.type)!=PTP_USB_CONTAINER_DATA) {
ret = PTP_ERROR_DATA_EXPECTED;
break;
} else
if (dtoh16(usbdata.code)!=ptp->Code) {
ret = dtoh16(usbdata.code);
break;
}
/* evaluate data length */
len=dtoh32(usbdata.length)-PTP_USB_BULK_HDR_LEN;
/* allocate memory for data */
*data=calloc(len,1);
/* copy first part of data to 'data' */
memcpy(*data,usbdata.payload.data,len);
/* is that all of data? */
if (len+PTP_USB_BULK_HDR_LEN<=sizeof(usbdata)) break;
/* if not finaly read the rest of it */
ret=params->read_func(((unsigned char *)(*data))+
PTP_USB_BULK_PAYLOAD_LEN,
len-PTP_USB_BULK_PAYLOAD_LEN,
params->data);
} while (0);
if (ret!=PTP_RC_OK) {
ptp_error (params,
"PTP: request code %4x container receive error %4x",
ptp->Code, ret);
ret = PTP_ERROR_IO;
}
return ret;
}
uint16_t
ptp_usb_getresp (PTPParams* params, PTPContainer* resp)
{
uint16_t ret;
PTPUSBBulkContainer usbresp;
PTP_CNT_INIT(usbresp);
/* read response, it should never be longer than sizeof(usbresp) */
ret=params->read_func((unsigned char *)&usbresp,
sizeof(usbresp), params->data);
if (ret!=PTP_RC_OK) {
ret = PTP_ERROR_IO;
} else
if (dtoh16(usbresp.type)!=PTP_USB_CONTAINER_RESPONSE) {
ret = PTP_ERROR_RESP_EXPECTED;
} else
if (dtoh16(usbresp.code)!=resp->Code) {
ret = dtoh16(usbresp.code);
}
if (ret!=PTP_RC_OK) {
ptp_error (params,
"PTP: request code 0x%4x getting resp error 0x%4x",
resp->Code, ret);
return ret;
}
/* build an appropriate PTPContainer */
resp->Code=dtoh16(usbresp.code);
resp->SessionID=params->session_id;
resp->Transaction_ID=dtoh32(usbresp.trans_id);
resp->Param1=dtoh32(usbresp.payload.params.param1);
resp->Param2=dtoh32(usbresp.payload.params.param2);
resp->Param3=dtoh32(usbresp.payload.params.param3);
resp->Param4=dtoh32(usbresp.payload.params.param4);
resp->Param5=dtoh32(usbresp.payload.params.param5);
return ret;
}
// major PTP functions
// Transaction data phase description
#define PTP_DP_NODATA 0x0000 // No Data Phase
#define PTP_DP_SENDDATA 0x0001 // sending data
#define PTP_DP_GETDATA 0x0002 // geting data
#define PTP_DP_DATA_MASK 0x00ff // data phase mask
// Number of PTP Request phase parameters
#define PTP_RQ_PARAM0 0x0000 // zero parameters
#define PTP_RQ_PARAM1 0x0100 // one parameter
#define PTP_RQ_PARAM2 0x0200 // two parameters
#define PTP_RQ_PARAM3 0x0300 // three parameters
#define PTP_RQ_PARAM4 0x0400 // four parameters
#define PTP_RQ_PARAM5 0x0500 // five parameters
/**
* ptp_transaction:
* params: PTPParams*
* PTPContainer* ptp - general ptp container
* uint16_t flags - lower 8 bits - data phase description
* unsigned int sendlen - senddata phase data length
* char** data - send or receive data buffer pointer
*
* Performs PTP transaction. ptp is a PTPContainer with appropriate fields
* filled in (i.e. operation code and parameters). It's up to caller to do
* so.
* The flags decide thether the transaction has a data phase and what is its
* direction (send or receive).
* If transaction is sending data the sendlen should contain its length in
* bytes, otherwise it's ignored.
* The data should contain an address of a pointer to data going to be sent
* or is filled with such a pointer address if data are received depending
* od dataphase direction (send or received) or is beeing ignored (no
* dataphase).
* The memory for a pointer should be preserved by the caller, if data are
* beeing retreived the appropriate amount of memory is beeing allocated
* (the caller should handle that!).
*
* Return values: Some PTP_RC_* code.
* Upon success PTPContainer* ptp contains PTP Response Phase container with
* all fields filled in.
**/
static uint16_t
ptp_transaction (PTPParams* params, PTPContainer* ptp,
uint16_t flags, unsigned int sendlen, char** data)
{
if ((params==NULL) || (ptp==NULL))
return PTP_ERROR_BADPARAM;
ptp->Transaction_ID=params->transaction_id++;
ptp->SessionID=params->session_id;
// send request
CHECK_PTP_RC(params->sendreq_func (params, ptp));
// is there a dataphase?
switch (flags&PTP_DP_DATA_MASK) {
case PTP_DP_SENDDATA:
CHECK_PTP_RC(params->senddata_func(params, ptp,
*data, sendlen));
break;
case PTP_DP_GETDATA:
CHECK_PTP_RC(params->getdata_func(params, ptp,
(unsigned char**)data));
break;
case PTP_DP_NODATA:
break;
default:
return PTP_ERROR_BADPARAM;
}
// get response
CHECK_PTP_RC(params->getresp_func(params, ptp));
return PTP_RC_OK;
}
// Enets handling functions
// PTP Events wait for or check mode
#define PTP_EVENT_CHECK 0x0000 // waits for
#define PTP_EVENT_CHECK_FAST 0x0001 // checks
static inline uint16_t
ptp_usb_event (PTPParams* params, PTPContainer* event, int wait)
{
uint16_t ret;
PTPUSBEventContainer usbevent;
if ((params==NULL) || (event==NULL))
return PTP_ERROR_BADPARAM;
switch(wait) {
case PTP_EVENT_CHECK:
ret=params->check_int_func((unsigned char*)&usbevent,
sizeof(usbevent), params->data);
break;
case PTP_EVENT_CHECK_FAST:
ret=params->check_int_fast_func((unsigned char*)
&usbevent, sizeof(usbevent), params->data);
break;
default:
ret=PTP_ERROR_BADPARAM;
}
if (ret!=PTP_RC_OK) {
ret = PTP_ERROR_IO;
ptp_error (params,
"PTP: reading event an error 0x%4x occured", ret);
/* reading event error is nonfatal (for example timeout) */
}
/* if we read anything over interrupt endpoint it must be an event */
/* build an appropriate PTPContainer */
PTP_CNT_INIT(event);
event->Code=dtoh16(usbevent.code);
event->SessionID=params->session_id;
event->Transaction_ID=dtoh32(usbevent.trans_id);
event->Param1=dtoh32(usbevent.param1);
event->Param2=dtoh32(usbevent.param2);
event->Param3=dtoh32(usbevent.param3);
return ret;
}
uint16_t
ptp_usb_event_check (PTPParams* params, PTPContainer* event) {
return ptp_usb_event (params, event, PTP_EVENT_CHECK_FAST);
}
uint16_t
ptp_usb_event_wait (PTPParams* params, PTPContainer* event) {
return ptp_usb_event (params, event, PTP_EVENT_CHECK);
}
/**
* PTP operation functions
*
* all ptp_ functions should take integer parameters
* in host byte order!
**/
/**
* ptp_getdeviceinfo:
* params: PTPParams*
*
* Gets device info dataset and fills deviceinfo structure.
*
* Return values: Some PTP_RC_* code.
**/
uint16_t
ptp_getdeviceinfo (PTPParams* params, PTPDeviceInfo* deviceinfo)
{
uint16_t ret;
PTPContainer ptp;
char* di=NULL;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_GetDeviceInfo;
ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &di);
ptp_unpack_DI(params, di, deviceinfo);
free(di);
return ret;
}
/**
* ptp_opensession:
* params: PTPParams*
* session - session number
*
* Establishes a new session.
*
* Return values: Some PTP_RC_* code.
**/
uint16_t
ptp_opensession (PTPParams* params, uint32_t session)
{
uint16_t ret;
PTPContainer ptp;
ptp_debug(params,"PTP: Opening session");
/* SessonID field of the operation dataset should be set allways
set to 0 for OpenSession request */
params->session_id=0x00000000;
/* TransactionID should be set to 0 also */
params->transaction_id=0x0000000;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_OpenSession;
ptp.Param1=session;
ret=ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL);
/* now set the global session id to current session number */
params->session_id=session;
return ret;
}
/**
* ptp_closesession:
* params: PTPParams*
*
* Closes session.
*
* Return values: Some PTP_RC_* code.
**/
uint16_t
ptp_closesession (PTPParams* params)
{
PTPContainer ptp;
ptp_debug(params,"PTP: Closing session");
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_CloseSession;
return ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL);
}
/**
* ptp_getststorageids:
* params: PTPParams*
*
* Gets array of StorageiDs and fills the storageids structure.
*
* Return values: Some PTP_RC_* code.
**/
uint16_t
ptp_getstorageids (PTPParams* params, PTPStorageIDs* storageids)
{
uint16_t ret;
PTPContainer ptp;
char* sids=NULL;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_GetStorageIDs;
ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &sids);
ptp_unpack_SIDs(params, sids, storageids);
free(sids);
return ret;
}
/**
* ptp_getststorageinfo:
* params: PTPParams*
* storageid - StorageID
*
* Gets StorageInfo dataset of desired storage and fills storageinfo
* structure.
*
* Return values: Some PTP_RC_* code.
**/
uint16_t
ptp_getstorageinfo (PTPParams* params, uint32_t storageid,
PTPStorageInfo* storageinfo)
{
uint16_t ret;
PTPContainer ptp;
char* si=NULL;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_GetStorageInfo;
ptp.Param1=storageid;
ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &si);
ptp_unpack_SI(params, si, storageinfo);
free(si);
return ret;
}
/**
* ptp_getobjecthandles:
* params: PTPParams*
* storage - StorageID
* objectformatcode - ObjectFormatCode (optional)
* associationOH - ObjectHandle of Association for
* wich a list of children is desired
* (optional)
* objecthandles - pointer to structute
*
* Fills objecthandles with structure returned by device.
*
* Return values: Some PTP_RC_* code.
**/
uint16_t
ptp_getobjecthandles (PTPParams* params, uint32_t storage,
uint32_t objectformatcode, uint32_t associationOH,
PTPObjectHandles* objecthandles)
{
uint16_t ret;
PTPContainer ptp;
char* oh=NULL;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_GetObjectHandles;
ptp.Param1=storage;
ptp.Param2=objectformatcode;
ptp.Param3=associationOH;
ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &oh);
ptp_unpack_OH(params, oh, objecthandles);
free(oh);
return ret;
}
uint16_t
ptp_getobjectinfo (PTPParams* params, uint32_t handle,
PTPObjectInfo* objectinfo)
{
uint16_t ret;
PTPContainer ptp;
char* oi=NULL;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_GetObjectInfo;
ptp.Param1=handle;
ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &oi);
ptp_unpack_OI(params, oi, objectinfo);
free(oi);
return ret;
}
uint16_t
ptp_getobject (PTPParams* params, uint32_t handle, char** object)
{
PTPContainer ptp;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_GetObject;
ptp.Param1=handle;
return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object);
}
uint16_t
ptp_getthumb (PTPParams* params, uint32_t handle, char** object)
{
PTPContainer ptp;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_GetThumb;
ptp.Param1=handle;
return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object);
}
/**
* ptp_deleteobject:
* params: PTPParams*
* handle - object handle
* ofc - object format code (optional)
*
* Deletes desired objects.
*
* Return values: Some PTP_RC_* code.
**/
uint16_t
ptp_deleteobject (PTPParams* params, uint32_t handle,
uint32_t ofc)
{
PTPContainer ptp;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_DeleteObject;
ptp.Param1=handle;
ptp.Param2=ofc;
return ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL);
}
/**
* ptp_sendobjectinfo:
* params: PTPParams*
* uint32_t* store - destination StorageID on Responder
* uint32_t* parenthandle - Parent ObjectHandle on responder
* uint32_t* handle - see Return values
* PTPObjectInfo* objectinfo- ObjectInfo that is to be sent
*
* Sends ObjectInfo of file that is to be sent via SendFileObject.
*
* Return values: Some PTP_RC_* code.
* Upon success : uint32_t* store - Responder StorageID in which
* object will be stored
* uint32_t* parenthandle- Responder Parent ObjectHandle
* in which the object will be stored
* uint32_t* handle - Responder's reserved ObjectHandle
* for the incoming object
**/
uint16_t
ptp_sendobjectinfo (PTPParams* params, uint32_t* store,
uint32_t* parenthandle, uint32_t* handle,
PTPObjectInfo* objectinfo)
{
uint16_t ret;
PTPContainer ptp;
char* oidata=NULL;
uint32_t size;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_SendObjectInfo;
ptp.Param1=*store;
ptp.Param2=*parenthandle;
size=ptp_pack_OI(params, objectinfo, &oidata);
ret=ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &oidata);
free(oidata);
*store=ptp.Param1;
*parenthandle=ptp.Param2;
*handle=ptp.Param3;
return ret;
}
/**
* ptp_sendobject:
* params: PTPParams*
* char* object - contains the object that is to be sent
* uint32_t size - object size
*
* Sends object to Responder.
*
* Return values: Some PTP_RC_* code.
*
*/
uint16_t
ptp_sendobject (PTPParams* params, char* object, uint32_t size)
{
PTPContainer ptp;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_SendObject;
return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &object);
}
/**
* ptp_initiatecapture:
* params: PTPParams*
* storageid - destination StorageID on Responder
* ofc - object format code
*
* Causes device to initiate the capture of one or more new data objects
* according to its current device properties, storing the data into store
* indicated by storageid. If storageid is 0x00000000, the object(s) will
* be stored in a store that is determined by the capturing device.
* The capturing of new data objects is an asynchronous operation.
*
* Return values: Some PTP_RC_* code.
**/
uint16_t
ptp_initiatecapture (PTPParams* params, uint32_t storageid,
uint32_t ofc)
{
PTPContainer ptp;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_InitiateCapture;
ptp.Param1=storageid;
ptp.Param2=ofc;
return ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL);
}
/**
* ptp_ek_sendfileobjectinfo:
* params: PTPParams*
* uint32_t* store - destination StorageID on Responder
* uint32_t* parenthandle - Parent ObjectHandle on responder
* uint32_t* handle - see Return values
* PTPObjectInfo* objectinfo- ObjectInfo that is to be sent
*
* Sends ObjectInfo of file that is to be sent via SendFileObject.
*
* Return values: Some PTP_RC_* code.
* Upon success : uint32_t* store - Responder StorageID in which
* object will be stored
* uint32_t* parenthandle- Responder Parent ObjectHandle
* in which the object will be stored
* uint32_t* handle - Responder's reserved ObjectHandle
* for the incoming object
**/
uint16_t
ptp_ek_sendfileobjectinfo (PTPParams* params, uint32_t* store,
uint32_t* parenthandle, uint32_t* handle,
PTPObjectInfo* objectinfo)
{
uint16_t ret;
PTPContainer ptp;
char* oidata=NULL;
uint32_t size;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_EK_SendFileObjectInfo;
ptp.Param1=*store;
ptp.Param2=*parenthandle;
size=ptp_pack_OI(params, objectinfo, &oidata);
ret=ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &oidata);
free(oidata);
*store=ptp.Param1;
*parenthandle=ptp.Param2;
*handle=ptp.Param3;
return ret;
}
/**
* ptp_ek_sendfileobject:
* params: PTPParams*
* char* object - contains the object that is to be sent
* uint32_t size - object size
*
* Sends object to Responder.
*
* Return values: Some PTP_RC_* code.
*
*/
uint16_t
ptp_ek_sendfileobject (PTPParams* params, char* object, uint32_t size)
{
PTPContainer ptp;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_EK_SendFileObject;
return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &object);
}
/* devinfo testing functions */
int
ptp_operation_issupported(PTPParams* params, uint16_t operation)
{
int i=0;
for (;i<params->deviceinfo.OperationsSupported_len;i++) {
if (params->deviceinfo.OperationsSupported[i]==operation)
return 1;
}
return 0;
}
int
ptp_property_issupported(PTPParams* params, uint16_t property)
{
int i=0;
for (;i<params->deviceinfo.DevicePropertiesSupported_len;i++) {
if (params->deviceinfo.DevicePropertiesSupported[i]==property)
return 1;
}
return 0;
}