Partial support for Samsung SGH-D600
Marvin Schmidt <[email protected]> Sun, 05 Jul 2009 22:04:43 +0200
| Newsgroups | gmane.comp.mobile.bitpim.devel |
|---|---|
| Message-ID | <[email protected]> |
Hello,
quite some time ago I started implementing support for the Samsung
SGH-D600, unfortunately that phone died and I had to get a new one,
that's why I couldn't finish that project.
I still got all of my work, but I'm afraid I can't comment too precisely
on it, but I figured maybe someone would like to continue my work,
that's why I'm sending what I got here.
The first thing I had to do was changing
return ': '.join(resp[0].split(': ')[1:])
in the __send_at_and_get function in src/phone_detect.py to
return resp[0]
in order to get my phone recognized at all. I wish I could tell you more
about why it's necessary, but it's been too long ago and I'm not able to
test it anymore.
The second patch adds two parameters to the sendpbcommand in
src/phones/com_samsung_packet.py. I think to remember doing this because
the phone sends big amounts splitted in multiple packets. If a packet
ends in "#OK#\r\n" one would have to send "##>\r\n" to keep to transfer
going. Doing that in the subclass was awfully slow, that's why i moved
it to the commport class to be able to react faster. Unfortunately I'm
missing the changes i did to commport.py :-/
Besides those 2 things it's just the p_samsungsghd600.p, the
com_samsungsghd600.py and the entry in src/phones/__init__.py
I got some mails from people, who would like to see support for the
Samsung SGH-D600 in BitPim an since i can't really do much about it I
hope some who has the phone takes it from here.
If any information about the phone are needed, I can probably cram out
the references/logs/etc. I used, just ask me, i'll do my best to help.
Best regards,
Marvin Schmidt
------------------------------------------------------------------------------
_______________________________________________
BitPim-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bitpim-devel
001-phone-detection.patch
(text/x-patch, 517 B)
Index: src/phone_detect.py
===================================================================
--- src/phone_detect.py (revision 4748)
+++ src/phone_detect.py (working copy)
@@ -104,7 +104,8 @@
def __send_at_and_get(self, comm, cmd):
try:
resp=comm.sendatcommand(cmd)
- return ': '.join(resp[0].split(': ')[1:])
+ #return ': '.join(resp[0].split(': ')[1:])
+ return resp[0]
except:
return None
def __get_manufacturer(self, comm):
002-samsung-packet.patch
(text/x-patch, 1.3 KB)
Index: src/phones/com_samsung_packet.py
===================================================================
--- src/phones/com_samsung_packet.py (revision 4748)
+++ src/phones/com_samsung_packet.py (working copy)
@@ -147,7 +147,7 @@
response=self.comm.sendatcommand("#PMODE=0")
return True
- def sendpbcommand(self, request, responseclass, ignoreerror=False, fixup=None):
+ def sendpbcommand(self, request, responseclass, ignoreerror=False, fixup=None, getasone=False, autocontinue=True):
"""Similar to the sendpbcommand in com_sanyo and com_lg, except that
a list of responses is returned, one per line of information returned
from the phone"""
@@ -158,7 +158,13 @@
data=buffer.getvalue()
try:
- response_lines=self.comm.sendatcommand(data, ignoreerror=ignoreerror)
+ response_lines=self.comm.sendatcommand(data, ignoreerror=ignoreerror, autocontinue=autocontinue)
+ if getasone:
+ #self.log('getasone: ' + str(";".join(["%s" % (v) for v in response_lines])))
+ temp="\x0D\x0A".join(response_lines)
+ response_lines[:] = []
+ response_lines.append(temp)
+
except commport.ATError:
self.comm.success=False
self.mode=self.MODENONE
003-init-entry.patch
(text/x-patch, 680 B)
Index: src/phones/__init__.py
===================================================================
--- src/phones/__init__.py (revision 4748)
+++ src/phones/__init__.py (working copy)
@@ -372,6 +372,11 @@
'brand': b_samsung,
'helpid': None,
},
+ 'SGH-D600': { 'module': 'com_samsungsghd600',
+ 'brand': b_samsung,
+ #'carrier': [c_vzw],
+ 'helpid': None,
+ },
'SPH-A460': { 'module': 'com_samsungspha460',
'brand': b_samsung,
'helpid': helpids.ID_PHONE_SAMSUNGOTHERS,
p_samsungsghd600.py
(text/x-python, 181.1 KB) - not displayed
com_samsungsghd600.py
(text/x-python, 23.7 KB)
### BITPIM ### ### Copyright (C) 2009 Marvin Schmidt <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### ### This file is probably based in parts upon com_samsungscha670.py """Communicate with a Samsung SGH-D600""" # lib modules import re import sha import time # my modules import common import commport #import com_brew import com_samsung import com_samsung_packet import com_phone import conversions import fileinfo import nameparser import p_samsungsghd600 import prototypes import sms import cStringIO import bpcalendar numbertypetab=('cell', 'home','office','fax','other') #class Phone(com_etsi.Phone): class Phone(com_samsung_packet.Phone): "Talk to the Samsung SGH-D600 Cell Phone" desc="SGH-D600" serialsname='sghd600' protocolclass=p_samsungsghd600 parent_phone=com_samsung_packet.Phone def __init__(self, logtarget, commport): "Calls all the constructors and sets initial modes" com_samsung_packet.Phone.__init__(self, logtarget, commport) self.numbertypetab=numbertypetab self.mode=self.MODENONE getringtones=None def listfiles(self, dir=''): results={} bla=self.comm.sendatcommand("+FSCD=\"" + dir + "\""); self.log("Listing files in dir: '/"+dir+"'") dir="/"+dir req=self.protocolclass.filelistrequest() if len(dir): req.dir=dir else: req.dir="/" res=self.sendpbcommand(req, self.protocolclass.filelistresponse) for entry in res: results[entry.filename]={ 'name': entry.filename, 'type': 'file', 'size': entry.size, 'date': (0, "") } return results def listsubdirs(self, dir='', recurse=0): results={} self.log("Listing own subdirs in dir: '"+dir+"'") bla=self.comm.sendatcommand("+FSCD=\"" + dir + "\""); req=self.protocolclass.dirlistrequest() if len(dir): req.dir=dir else: req.dir="/" res=self.sendpbcommand(req, self.protocolclass.dirlistresponse) for entry in res: subdir=entry.name if len(dir): subdir=dir+"/"+subdir else: subdir="/"+subdir results[subdir]={ 'name': subdir, 'type': 'directory' } #if recurse: #for k,_subdir in results.items(): #results.update(self.listsubdirs(_subdir['name'], recurse-1)) return results def getfilecontents(self, file, use_cache=False): """ if use_cache: node=self.statfile(file) if node and file_cache.hit(file, node['date'][0], node['size']): self.log('Reading from cache: '+file) _data=file_cache.data(file) if _data: return _data self.log('Cache file corrupted and discarded') """ start=time.time() self.log("Getting file contents '"+file+"'") desc="Reading "+file req=self.protocolclass.fsinforequest() res=self.sendpbcommand(req, self.protocolclass.fsinforesponse) """ req=self.protocolclass.filerequest() req.filename=file res=self.sendpbcommand(req, self.protocolclass.fileresponse, autocontinue=False) """ try: response_lines=self.comm.sendatcommand("+FSFR=-1,\"" + file + "\"", ignoreerror=False, autocontinue=False) except commport.ATError: self.comm.success=False self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") self.comm.success=True self.log('response_lines: ' + str(response_lines)) data=cStringIO.StringIO() if len(response_lines) > 1: self.log("short way") res=self.protocolclass.fileresponse() line=response_lines.pop(0) buffer=prototypes.buffer(line) res.readfrombuffer(buffer, logtitle="Samsung phonebook response") filesize=res.size for line in response_lines: res=self.protocolclass.downloadpacket() buffer=prototypes.buffer(line) res.readfrombuffer(buffer, logtitle="Samsung phonebook response") data.write(res.data) data=data.getvalue() return data; self.log("long way") res=self.protocolclass.fileresponse() buffer=prototypes.buffer(response_lines[0][:-4]) res.readfrombuffer(buffer, logtitle="Samsung phonebook response") filesize=res.size #req=self.protocolclass.continuerequest() #res=self.sendpbcommand(req, self.protocolclass.download) try: charsread=0 tries=0 self.comm.write("##>\r\n") res="" while True: b=self.comm.ser.inWaiting() if b: read=self.comm.read(b,0) if read.find("OK\r")>=0 or (res.find("ERROR\r")>=0): break if read.find("#OK#\r\n"): res=read[:-6] self.log("myres: " + str(read[:-6])) #dl=self.protocolclass.downloadpacket() #buffer=prototypes.buffer(read[:-6]) #dl.readfrombuffer(buffer, logtitle="Samsung phonebook response") self.comm.write("##>\r\n") continue r=self.comm.read(1,0) if len(r): res=res+r continue break data.write(res) return data.getvalue() """ while True: b=self.comm.ser.inWaiting() if b == 0: #if tries > 3: # self.log("3 tries...") # break #tries+=1 continue data=self.comm._read(b, True) if data.endswith("#OK#\r\n"): #res=self.protocolclass.downloadpacket() #moo=data[2:] #moo=moo[:-6] #buffer=prototypes.buffer(moo) #res.readfrombuffer(buffer, logtitle="Samsung phonebook response") charsread+=512 self.progress(charsread, filesize, 'Reading file...') self.comm.write("##>\r\n") """ #self.log('readahead: ' + str(self.comm.readahead[:-6])) except commport.ATError: self.comm.success=False self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") #line=self.getcleanline() #self.log("getfile cleanline: " + str(line)) """ if line==fullline: line=self.getcleanline() while line!="OK" and line: if line=="ERROR": if not ignoreerror: raise ATError elif line.endswith("#OK#"): if autocontinue: res.append(line[:-4]) self.write(str("##>\r\n")) else: res.append(line) break try: self.readatresponse(ignoreerror) except CommTimeout: raise else: res.append(line) line=self.getcleanline() self.log("res: " + str(res)) """ """ * first packet looks like this: * +FSFR: 0,"test.jpg",106,"",0,513,0,"","",""#OK# * * @param 1 int ? * @param 2 qval filename * @param 3 int ? * @param 4 qval ? * @param 5 int ? * @param 6 int size * @param 7 int ? * @param 8 qval ? * @param 9 qval ? * @param 10 qval ? * * * after sending "##>\r\n": * +FSFR: 512,0,-587442163,<data> * * @param 1 int packet size * @param 2 int packet number * @param 3 int ? * """ #while (res.endswith("#OK#")) # self.comm.write("##>\r\n") # res=self.comm._read() res=[] #line=self.comm.getcleanline() #self.log("mycleanline: " + str(line)) """ if line==fullline: line=self.getcleanline() self.log("cleanline: " + str(line)) while line!="OK" and line: if line=="ERROR": if not ignoreerror: raise ATError elif line.endswith("#OK#"): if autocontinue: res.append(line[:-4]) self.write(str("##>\r\n")) else: res.append(line) break try: self.readatresponse(ignoreerror) except CommTimeout: raise else: res.append(line) line=self.getcleanline() self.log("cleanline: " + str(line)) self.log('res: ' + str(res)) """ #data=cStringIO.StringIO() """ req=p_brew.readfilerequest() req.filename=file res=self.sendbrewcommand(req, p_brew.readfileresponse) filesize=res.filesize data.write(res.data) counter=0 while res.thereismore: counter+=1 if counter>0xff: counter=0x01 if counter%5==0: self.progress(data.tell(), filesize, desc) req=p_brew.readfileblockrequest() req.blockcounter=counter res=self.sendbrewcommand(req, p_brew.readfileblockresponse) data.write(res.data) self.progress(1,1,desc) data=data.getvalue() # give the download speed if we got a non-trivial amount of data end=time.time() if end-start>3: self.log("Read "+`filesize`+" bytes at "+`int(filesize/(end-start))`+" bytes/second") if filesize!=len(data): self.log("expected size "+`filesize`+" actual "+`len(data)`) self.raisecommsexception("Brew file read is incorrect size", common.CommsDataCorruption) if use_cache and node: file_cache.add(file, node.get('date', [0])[0], data) """ #return data def getfundamentals(self, results): """Gets information fundamental to interoperating with the phone and UI. Currently this is: - 'uniqueserial' a unique serial number representing the phone - 'groups' the phonebook groups - 'wallpaper-index' map index numbers to names - 'ringtone-index' map index numbers to ringtone names This method is called before we read the phonebook data or before we write phonebook data. """ self.comm.setbaudrate(115200) # use a hash of ESN and other stuff (being paranoid) self.log("Retrieving fundamental phone information") self.log("Reading phone serial number") results['uniqueserial']=sha.new(self.get_esn()).hexdigest() req=self.protocolclass.inforequest() res=self.sendpbcommand(req, self.protocolclass.inforesponse) """ static groups, dunno how to read """ groups={} groups[0] = {'name': 'unassigned'} groups[1] = {'name': 'family'} groups[2] = {'name': 'office'} groups[3] = {'name': 'friends'} groups[4] = {'name': 'others'} results['groups']=groups self.log("Fundamentals retrieved") return results def getwallpapers(self, result): wallpapers = {} return wallpapers def getcalendar(self, result): entries = {} self.log("Getting calendar entries") # get number of entries req=self.protocolclass.organizerinforequest() res=self.sendpbcommand(req, self.protocolclass.organizerinforesponse) events = res[0].entries req=self.protocolclass.eventrequest() cal_cnt=0 for slot in range(events): req.slot=slot res=self.sendpbcommand(req,self.protocolclass.eventresponse) if len(res) > 0: self.progress(slot+1, events, res[0].eventname) # build a calendar entry entry=bpcalendar.CalendarEntry() # start time date entry.start=(res[0].start_year, res[0].start_month, res[0].start_day, res[0].start_hour, res[0].start_minute) if res[0].end_year: # valid end time entry.end=(res[0].end_year, res[0].end_month, res[0].end_day, res[0].end_hour, res[0].end_minute) else: entry.end=entry.start # description[location] entry.desc_loc=res[0].eventname try: alarm=self.__cal_alarm_values[res[0].alarm] except: alarm=None entry.alarm=alarm # update calendar dict entries[entry.id]=entry cal_cnt += 1 result['calendar']=entries return result def getphonebook(self, result): pbook={} count=0 #req=self.protocolclass.phonebookmemoryrequest() #res=self.sendpbcommand(req, self.protocolclass.phonebookmemoryresponse) self.comm.sendatcommand("+CPBS=\"ME\"", ignoreerror=False) # get count of phone book entries req=self.protocolclass.phonebookinforequest() res=self.sendpbcommand(req, self.protocolclass.phonebookinforesponse) entries=res[0].usedslots req=self.protocolclass.phonebookslotrequest() name="" surname="" for slot in range(1, entries+1): req.slot=slot res=self.sendpbcommand(req, self.protocolclass.phonebookslotresponse) #, fixup=self.pblinerepair) if len(res) > 0: name=res[0].entry.name name=name[1:len(name)-1] surname=res[0].entry.surname surname=surname[1:len(surname)-1] self.log('Slot #' + `slot` + ": " + name + ' ' + surname) entry=self.extractphonebookentry(res[0].entry, result) pbook[count]=entry count+=1 else: name="" surname="" self.progress(slot, entries, 'Reading entry %(slot)d: %(name)s'%{ 'slot': slot, 'name': surname + ', ' + name }) result['phonebook']=pbook cats=[] for i in result['groups']: if result['groups'][i]['name']!='Unassigned': cats.append(result['groups'][i]['name']) result['categories']=cats print "returning keys",result.keys() return pbook def extractphonebookentry(self, entry, fundamentals): res={} res['serials']=[ {'sourcetype': self.serialsname, 'slot': entry.slot, 'sourceuniqueid': fundamentals['uniqueserial']} ] # only one name #res['names']=[ {'first': entry.name, 'last': entry.surname} ] res['names']=[ {'full': entry.name[1:len(entry.name)-1].decode("utf8") + ' ' + entry.surname[1:len(entry.surname)-1].decode("utf8")} ] # only one category cat=fundamentals['groups'].get(entry.group, {'name': "Unassigned"})['name'] if cat!="Unassigned": res['categories']=[ {'category': cat} ] # only one email if len(entry.email): res['emails']=[ {'email': entry.email} ] # only one url if len(entry.url): res['urls']=[ {'url': entry.url} ] # separate the following processing into methods so subclass can # customize them self._extractphonebook_numbers(entry, fundamentals, res) #self._extractphonebook_ringtone(entry, fundamentals, res) #self._extractphonebook_wallpaper(entry, fundamentals, res) # We don't have a place to put these # print entry.name, entry.birthday # print entry.name, entry.timestamp return res def _extractphonebook_numbers(self, entry, fundamentals, res): """Extract and build phone numbers""" res['numbers']=[] secret=0 #speeddialtype=entry.speeddial numberindex=0 for type in self.numbertypetab: if len(entry.numbers[numberindex].number): numhash={'number': entry.numbers[numberindex].number, 'type': type } #if entry.numbers[numberindex].secret==1: # secret=1 #if speeddialtype==numberindex: # numhash['speeddial']=entry.uslot res['numbers'].append(numhash) numberindex+=1 # Field after each number is secret flag. Setting secret on # phone sets secret flag for every defined phone number #res['flags']=[ {'secret': secret} ] def getsms(self, result): storagetypes=("ME", "SM") sims={} for storage in storagetypes: req=self.protocolclass.select_message_storage_req() req.storage=storage res=self.sendpbcommand(req, self.protocolclass.select_message_storage_resp) self.log('storage: ' + req.storage + ': ' + str(res[0].used) + ' used / ' + str(res[0].total) + ' total') self.comm.sendatcommand("+CMGF=1", ignoreerror=False) self.comm.sendatcommand("+CSDH=1", ignoreerror=False) req=self.protocolclass.message_read_req() usedslots = res[0].used for slot in range(1, usedslots+1): req.slot = slot self.progress(slot, usedslots+1, 'Reading memory %(storage)s, message %(slot)d'%{ 'storage': storage, 'slot': slot }) try: res2=self.sendpbcommand(req, self.protocolclass.message_read_resp, getasone=True) #except: # pass _sms=sms.SMSEntry() if res2[0].msg_type==self.protocolclass.SMS_MSG_REC_UNREAD or \ res2[0].msg_type==self.protocolclass.SMS_MSG_REC_READ: # unread/read inbox _sms._from=res2[0].sender _sms.folder=sms.SMSEntry.Folder_Inbox _sms.read=res2[0].msg_type==self.protocolclass.SMS_MSG_REC_READ elif res2[0].msg_type==self.protocolclass.SMS_MSG_STO_SENT: # outbox #_sms.add_recipient(res[0].address) _sms.folder=sms.SMSEntry.Folder_Sent elif res2[0].msg_type==self.protocolclass.SMS_MSG_STO_UNSENT: # saved _sms.folder=sms.SMSEntry.Folder_Saved #_sms.add_recipient(res[0].address) else: self.log('Unknown message type: %s' % res2[0].msg_type) _sms=None if _sms: if res2[0].timestamp: _sms.datetime=res2[0].timestamp _sms.text=str(res2[0].rest).decode("utf8") #_sms.text=res2[0].rest result['sms'] = [] sims[_sms.id]=_sms except: pass #except: # if __debug__: # raise result['canned_msg']=[] result['sms']=sims return result def get_firmware_version(self): req=self.protocolclass.firmwareinforeq() res=self.sendpbcommand(req, self.protocolclass.firmwareinforesp) return res[0].firmware def get_signal_quality(self): req=self.protocolclass.signalreq() res=self.sendpbcommand(req, self.protocolclass.signalresp) #return res[0].signal if res[0].signal is not None: return str(100*int(res[0].signal)/31)+'%' class Profile(com_samsung.Profile): serialsname='sghd600' WALLPAPER_WIDTH=128 WALLPAPER_HEIGHT=128 MAX_WALLPAPER_BASENAME_LENGTH=19 WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_ ." ## WALLPAPER_CONVERT_FORMAT="bmp" usbids=( ( 0x04e8, 0x663e, 2),) WALLPAPER_CONVERT_FORMAT="jpg" MAX_RINGTONE_BASENAME_LENGTH=19 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_ ." RINGTONE_LIMITS= { 'MAXSIZE': 30000 } # use for auto-detection phone_manufacturer='SAMSUNG' phone_model='SAMSUNG SGH-D600' def __init__(self): com_samsung.Profile.__init__(self) """ _supportedsyncs=( ('phonebook', 'read', None), # all phonebook reading ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook ('calendar', 'read', None), # all calendar reading ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar ('ringtone', 'read', None), # all ringtone reading ('ringtone', 'write', 'OVERWRITE'), ('wallpaper', 'read', None), # all wallpaper reading ('wallpaper', 'write', 'OVERWRITE'), ('memo', 'read', None), # all memo list reading DJP ('memo', 'write', 'OVERWRITE'), # all memo list writing DJP ('todo', 'read', None), # all todo list reading DJP ('todo', 'write', 'OVERWRITE'), # all todo list writing DJP ('sms', 'read', None), # all SMS list reading DJP ) """ _supportedsyncs=( ('phonebook', 'read', None), # all phonebook reading ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook ('calendar', 'read', None), # all calendar reading ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar ('todo', 'read', None), # all todo list reading ('todo', 'write', 'OVERWRITE'), # only overwriting calendar ('wallpaper', 'read', None), ('wallpaper', 'write', 'OVERWRITE'), ('sms', 'read', None), ) if __debug__: _supportedsyncs+=(('sms', 'write', 'OVERWRITE'),) def convertphonebooktophone(self, helper, data): return data __audio_ext={ 'MIDI': 'mid', 'PMD': 'pmd', 'QCP': 'pmd' } def QueryAudio(self, origin, currentextension, afi): # we don't modify any of these if afi.format in ("MIDI", "PMD", "QCP"): for k,n in self.RINGTONE_LIMITS.items(): setattr(afi, k, n) return currentextension, afi d=self.RINGTONE_LIMITS.copy() d['format']='QCP' return ('pmd', fileinfo.AudioFileInfo(afi, **d)) imageorigins={} imageorigins.update(common.getkv(com_samsung.Profile.stockimageorigins, "images")) imagetargets={} imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "wallpaper", {'width': 128, 'height': 128, 'format': "PNG"})) imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "fullscreen", {'width': 128, 'height': 160, 'format': "PNG"})) imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "pictureid", {'width': 96, 'height': 96, 'format': "JPEG"})) def GetImageOrigins(self): # Note: only return origins that you can write back to the phone return self.imageorigins def GetTargetsForImageOrigin(self, origin): # right now, supporting just 'images' origin if origin=='images': return self.imagetargets