commit b1adffb30f83564a595cc2118d0f256afa0195df Author: UzixLS Date: Mon Sep 21 10:11:42 2015 +0300 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20b7020 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.settings +.cproject +.project +.externalToolBuilders/ +ehs5fs diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b315afa --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +CWARNS = -Wall -Wextra -Wconversion -Wno-unused-parameter +CFLAGS = -std=gnu11 -fmessage-length=0 -O2 -g3 +CDEFS = -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26 +LDFLAGS = -lfuse + +all: + $(CC) $(CWARNS) $(CFLAGS) $(CDEFS) $(LDFLAGS) *.c -o ehs5fs + +clean: + rm ehs5fs diff --git a/fuse.c b/fuse.c new file mode 100644 index 0000000..8dfa0dc --- /dev/null +++ b/fuse.c @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include +#include +#include "main.h" +#include "fuse.h" +#include "modem.h" + + +static int fs_getattr( const char *path, struct stat *st ) +{ + int ret; + printf( "fs_getattr %s\n", path ); + + if( 0 == strcmp(path, "/") ) { + st->st_mode = S_IFDIR | 0777; + st->st_nlink = 2; + } else { + st->st_nlink = 1; + ret = m_stat( &M, path, st ); + if( ret == ENOENT ) return -ret; + if( ret < 0 ) { + printf( "fs_getattr: tc_stat err ret=%d'\n", ret ); + return 0; + } + } + return 0; +} + + +static int fs_readdir( + const char *path, + void *buf, + fuse_fill_dir_t filler, + off_t offset, + struct fuse_file_info *fi ) +{ + printf( "fs_readdir %s\n", path ); + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + m_ls( &M, path, buf, filler ); + return 0; +} + + +static int fs_read( + const char *path, + char *buf, + size_t size, + off_t offset, + struct fuse_file_info *fi ) +{ + int ret = 0; + int readchunk = min(MAX_SIZE, (int)size); + int readed = 0; + int left = 0; + struct stat st; + + printf( "fs_read %s (%u+%lld)\n", path, size, offset); + int fh = m_open( &M, path, 0 ); + if( fh < 0 ) return -1; + if( m_seek( &M, fh, (int)offset, 0) < 0 ) { ret = -EIO; goto fs_read_exit; } + if( m_stat( &M, path, &st ) < 0 ) { ret = -EIO; goto fs_read_exit; } + left = min( (int)size, (int)st.st_size - (int)offset ); + + while( left > 0 ) { + int bytes = min( (int)size, left ); + if( bytes > readchunk ) bytes = readchunk; + if( m_read( &M, fh, (unsigned)bytes, buf + readed ) < 0 ) { + ret = -1; goto fs_read_exit; + } + readed += bytes; + left -= bytes; + printf( "fs_read readed %d bytes from %d (%d left)\n", readed, size, left ); + usleep( 350 ); + + } + ret = readed; + +fs_read_exit: + m_close( &M, fh ); + printf( "fs_read ret=%d\n", ret ); + return ret; +} + + +static int fs_write( + const char *path, + const char *buf, + size_t size, + off_t offset, + struct fuse_file_info *fi ) +{ + int ret = 0; + int left = (int)size; + int written = 0; + + printf( "fs_write %s (%u+%lld)\n", path, size, offset ); + int fh = m_open( &M, path, FLAG_APPEND ); + if( fh < 0 ) return -1; + if( m_seek( &M, fh, (int)offset, 0) < 0 ) { ret = -EIO; goto fs_write_exit; } + + while( left > 0 ) { + int bytes = min( MAX_SIZE, left ); + if( m_write( &M, fh, (unsigned)bytes, buf + written ) < 0 ) { + ret = -EIO; goto fs_write_exit; + } + written += bytes; + left -= bytes; + printf( "fs_write writed %d bytes from %d (%d left)\n", written, size, left ); + } + ret = written; + +fs_write_exit: + m_close( &M, fh ); + printf( "fs_write ret=%d\n", ret ); + return ret; +} + + +static int fs_mknod( + const char *path, + mode_t mode, + dev_t dev ) +{ + int fh = m_open( &M, path, FLAG_CREATE ); + if( fh < 0 ) return -EIO; + m_close( &M, fh ); + return 0; +} + + +static int fs_mkdir( const char *path, mode_t mode ) +{ + if( m_mkdir( &M, path ) < 0 ) + return -EIO; + else + return 0; +} + +static int fs_rmdir( const char *path ) +{ + if( m_rmdir( &M, path ) < 0 ) + return -EIO; + else + return 0; +} + + +static int fs_unlink( const char *path ) +{ + if( m_remove( &M, path ) < 0 ) + return -EIO; + else + return 0; +} + + +static int fs_truncate( const char *path, off_t offset ) +{ + if( offset != 0 ) return -EFAULT; + int fh = m_open( &M, path, FLAG_TRUNCATE ); + if( fh < 0 ) return -EIO; + m_close( &M, fh ); + return 0; +} + + +static int fs_rename( const char *oldpath, const char *newpath ) +{ + int saferename = 1; + char *ptr_oldpath, *ptr_newpath; + + ptr_oldpath = strrchr( oldpath, '/' ); + ptr_newpath = strrchr( newpath, '/' ); + if( strncmp( oldpath, newpath, (unsigned)(max( ptr_oldpath - oldpath, ptr_newpath - newpath )) + ) != 0 ) + saferename = 1; + + if( ptr_oldpath[1] == '.' || ptr_newpath[1] == '.' ) + saferename = 1; + + if( saferename == 0 ) { + if( m_rename( &M, oldpath+1, newpath+1 ) < 0 ) return -EIO; + } else { + if( m_copy( &M, oldpath, newpath ) < 0 ) return -EIO; + if( m_remove( &M, oldpath ) < 0 ) return -EIO; + } + return 0; +} + + +static int fs_statfs( const char *path, struct statvfs *st ) +{ + if( m_gstat( &M, st ) < 0 ) + return -EIO; + else + return 0; +} + + +struct fuse_operations fuse_ops = { + .getattr = fs_getattr, + .readdir = fs_readdir, + .read = fs_read, + .write = fs_write, + .mknod = fs_mknod, + .mkdir = fs_mkdir, + .rmdir = fs_rmdir, + .unlink = fs_unlink, + .truncate = fs_truncate, + .rename = fs_rename, + .statfs = fs_statfs, +}; diff --git a/fuse.h b/fuse.h new file mode 100644 index 0000000..e64dfae --- /dev/null +++ b/fuse.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern struct fuse_operations fuse_ops; diff --git a/main.c b/main.c new file mode 100644 index 0000000..e998339 --- /dev/null +++ b/main.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include "fuse.h" +#include "modem.h" + +struct modem M; + + +struct { + char *point; + char *dev; + char *mountpoint; + int readonly; + int debug; +} options = { + .point = NULL, + .dev = NULL, + .readonly = 0, + .debug = 0, +}; +enum { + KEY_VERSION, + KEY_HELP, + KEY_PORT, + KEY_READONLY, + KEY_DEBUG, +}; +static struct fuse_opt opts[] = { + FUSE_OPT_KEY("-V", KEY_VERSION), + FUSE_OPT_KEY("--version", KEY_VERSION), + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("--readonly", KEY_READONLY), + FUSE_OPT_KEY("--debug", KEY_DEBUG), + FUSE_OPT_END +}; + +const char *usage = "\ +usage: tc65fs [fuse options] device mountpoint\n\ + -V --version show software version inforamtion\n\ + --port=/dev/ttyS0 connect to specified port\n\ + --readonly disable any writting operations\n\ + --debug start in debug mode\n\ +"; + + +static int opt_proc( + void *data, + const char *arg, + int key, + struct fuse_args *outargs ) +{ + switch (key) { + case KEY_VERSION: + printf("ehs5fs build at %s %s, (c)2015 Eugene Lozovoy \n", __DATE__, __TIME__); + exit( 1 ); + case KEY_HELP: + printf( usage ); + exit( 0 ); + case FUSE_OPT_KEY_NONOPT: + if (options.dev == NULL) { + options.dev = strdup(arg); + return 0; + } + if (options.point == NULL) { + options.point = strdup(arg); + return 0; + } + return 1; + case KEY_PORT: + if (options.dev == NULL) { + options.dev = strdup(arg+11); + return 0; + } + return 1; + case KEY_READONLY: + options.readonly = 1; + return 0; + case KEY_DEBUG: + options.debug = 1; + return 0; + } + return 0; +} + + +int main( int argc, char *argv[] ) +{ + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + + if( fuse_opt_parse( &args, NULL, opts, opt_proc ) ) return 1; + fuse_opt_add_arg( &args, "-s" ); + if( options.debug ) fuse_opt_add_arg( &args, "-d" ); + if( options.readonly ) fuse_opt_add_arg( &args, "-oro" ); + if( options.point ) fuse_opt_add_arg( &args, options.point ); + if( !options.point || !options.dev ) { + printf( usage ); + return -2; + } + + if( m_init( &M, options.dev ) < 0 ) { + printf( "m_init failed!\n" ); + return -1; + } + + return fuse_main( args.argc, args.argv, &fuse_ops, NULL ); +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..b8c8bfa --- /dev/null +++ b/main.h @@ -0,0 +1,8 @@ +#pragma once + +#include "modem.h" + +#define max(a,b) ((a)>(b))?(a):(b) +#define min(a,b) ((a)<(b))?(a):(b) + +extern struct modem M; diff --git a/modem.c b/modem.c new file mode 100644 index 0000000..23ec7f4 --- /dev/null +++ b/modem.c @@ -0,0 +1,354 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "modem.h" + + +static int call( + struct modem *m, + const char *cmd, + unsigned cmdlen, + char *retbuf, + unsigned retmaxbytes, + unsigned *retbytes ) +{ + #define call_checkstr(str, retcode) \ + if( bytes >= sizeof(str)-1 && strncmp( &(retbuf[bytes-sizeof(str)+1]), str, sizeof(str)-1 ) == 0 ) { \ + ret = retcode; \ + bytes -= sizeof(str)-1; \ + break; \ + } + + unsigned bytes = 0; + int ret = 0; + if( m == NULL || cmd == NULL ) return -1; + + if( cmdlen == 0 ){ + printf( "call write [[[%s]]]\n", cmd ); + cmdlen = strlen( cmd ); + } else { + printf( "call write %u raw bytes\n", cmdlen ); + } + + tcflush( m->fd, TCIOFLUSH ); + while( cmdlen-- ) { + if( write( m->fd, cmd, 1 ) != 1 ) { + perror( "call write error" ); + return -1; + } + cmd++; + } + fsync( m->fd ); + + if( retmaxbytes < 1 || retbuf == NULL ) return 0; + while( bytes < retmaxbytes ) { + if( read( m->fd, &(retbuf[bytes]), 1 ) != 1 ) break; + bytes++; + call_checkstr( "\r\nOK\r\n", 1 ); + call_checkstr( "\r\nERROR\r\n", -2 ); + call_checkstr( "\r\nCONNECT\r\n", 1 ); + } + retbuf[bytes] = 0; + if( retbytes != NULL ) *retbytes = bytes; + printf( "call read %d bytes (from %d) ret=%d [[[%s]]]\n", bytes, retmaxbytes, ret, retbuf ); + return ret; +} + + +int m_ls( struct modem *m, const char *path, void *fusebuf, fuse_fill_dir_t fusefiller ) +{ + char buf[1024], scanbuf[128]; + char *bufptr = buf; + if( m == NULL || path == NULL || fusebuf == NULL || fusefiller == NULL ) return -1; + + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"ls\",\"a:/%s\"\r\n", path ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) return -1; + + while( 1 ) { + strsep( &bufptr, "\n" ); + if( bufptr == NULL ) break; + scanbuf[0] = 0; + if( sscanf( bufptr, "^SFSA: \"%s\"\r\n", scanbuf ) != 1) continue; + if( strlen( scanbuf ) < 2 ) continue; + scanbuf[strlen(scanbuf)-1] = 0; + if( scanbuf[strlen(scanbuf)-1] == '/' ) + scanbuf[strlen(scanbuf)-1] = 0; + if( fusebuf != NULL && fusefiller != NULL ) + fusefiller( fusebuf, scanbuf, NULL, 0 ); + } + + return 0; +} + + +int m_stat( struct modem *m, const char *path, struct stat *fusest ) +{ + char buf[1024]; + char *bufptr = buf; + if( m == NULL || path == NULL || fusest == NULL ) return -1; + + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"stat\",\"a:/%s\"\r\n", path ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) { + int errcode; + strsep( &bufptr, "\n" ); + if( bufptr == NULL ) return -1; + if( sscanf( bufptr, "^SFSA: %d\r\n", &errcode ) != 1 ) return -1; + printf( "m_stat errcode %d\n", errcode ); + switch( errcode ) { + case RESULT_NOTFOUND: + return ENOENT; + default: + return EIO; + } + } + + for( int i = 0; i < 6; i++ ) { + int tmp; + struct tm tp; + strsep( &bufptr, "\n" ); + if( bufptr == NULL ) return -3; + + switch( i ) { + case 1: + case 2: + case 3: + if( sscanf( bufptr, "^SFSA: \"%d/%d/%d,%d:%d:%d\"\r\n", + &(tp.tm_year), &(tp.tm_mon), &(tp.tm_mday), + &(tp.tm_hour), &(tp.tm_min), &(tp.tm_sec) ) != 6 ) return -4-i; + tp.tm_year += 100; + } + + switch( i ) { + case 0: + if( sscanf( bufptr, "^SFSA: %d\r\n", &tmp ) != 1 ) return -4; + fusest->st_size = tmp; + break; + case 1: + fusest->st_atim.tv_sec = mktime( &tp ); + break; + case 2: + fusest->st_mtim.tv_sec = mktime( &tp ); + break; + case 3: + fusest->st_ctim.tv_sec = mktime( &tp ); + break; + case 4: + if( sscanf( bufptr, "^SFSA: %d\r\n", &tmp ) != 1 ) return -8; + if( tmp == ATTR_FILE ) + fusest->st_mode = S_IFREG | 0666; + else + fusest->st_mode = S_IFDIR | 0777; + break; + } + } + + return 0; +} + + +int m_read( struct modem *m, int fh, unsigned size, char *outbuf ) +{ + char buf[MAX_SIZE+100]; + char *bufptr = buf; + + if ( m == NULL ) return -1; + printf( "m_read %d (%u)\n", fh, size ); + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"read\",%d,%u\r\n", fh, size ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) return -2; + + strsep( &bufptr, "\n" ); + strsep( &bufptr, "\n" ); + if( bufptr == NULL ) return -3; + + if( outbuf != NULL ) + memcpy( outbuf, bufptr, size ); + + return 0; +} + + +int m_seek( struct modem *m, int fh, int offset, enum seek flags ) +{ + char buf[100]; + if( m == NULL ) return -1; + printf( "m_seek %d to %d\n", fh, offset ); + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"seek\",%d,%d,%d\r\n", fh, offset, flags ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) return -2; + return 0; +} + + +int m_open( struct modem *m, const char *path, enum flags flags ) +{ + int fh = -1; + char buf[100]; + char *bufptr = buf; + + if( m == NULL || path == NULL ) return -1; + printf( "m_open %s\n", path ); + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"open\",\"a:/%s\",%u\r\n", path, flags ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) return -1; + + strsep( &bufptr, "\n" ); + if( bufptr == NULL ) return -2; + if( sscanf( bufptr, "^SFSA: %d,0\r\n", &fh ) != 1 ) { + printf( "m_open sscanf failed\n" ); + return -3; + } + printf( "m_open ret %d\n", fh ); + return fh; +} + + +int m_close( struct modem *m, int fh ) +{ + char buf[100]; + if( m == NULL ) return -1; + printf( "m_close #%d\n", fh ); + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"close\",%d\r\n", fh ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) return -2; + return 0; +} + + +int m_write( struct modem *m, int fh, unsigned size, const char *data ) +{ + char buf[size+100]; + if( m == NULL || data == NULL ) return -1; + printf( "m_write %u bytes to #%d\n", size, fh ); + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"write\",%d,%u\r\n", fh, size ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) < 0 ) return -2; + if( call( m, data, size, buf, size, NULL ) < 0 ) return -3; + return (int)size; +} + +int m_remove( struct modem *m, const char *path ) +{ + char buf[100]; + if( m == NULL || path == NULL ) return -1; + printf( "m_remove %s\n", path ); + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"remove\",\"a:/%s\"\r\n", path ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) return -1; + return 0; +} + +int m_mkdir( struct modem *m, const char *path ) +{ + char buf[100]; + if( m == NULL || path == NULL ) return -1; + printf( "m_mkdir %s\n", path ); + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"mkdir\",\"a:/%s\"\r\n", path ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) return -1; + return 0; +} + + +int m_rmdir( struct modem *m, const char *path ) +{ + char buf[100]; + if( m == NULL || path == NULL ) return -1; + printf( "m_rmdir %s\n", path ); + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"rmdir\",\"a:/%s\"\r\n", path ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) return -1; + return 0; +} + + +int m_copy( struct modem *m, const char *srcpath, const char *dstpath ) +{ + char buf[100]; + if( m == NULL || srcpath == NULL || dstpath == NULL ) return -1; + printf( "m_copy %s to %s\n", srcpath, dstpath ); + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"copy\",\"a:/%s\",\"a:/%s\"\r\n", srcpath, dstpath ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) return -1; + return 0; +} + + +int m_rename( struct modem *m, const char *path, const char *newname ) +{ + char buf[100]; + if( m == NULL || path == NULL || newname == NULL ) return -1; + printf( "m_rename %s to %s\n", path, newname ); + snprintf( buf, sizeof(buf)-1, "AT^SFSA=\"rename\",\"a:/%s\",\"%s\"\r\n", path, newname ); + if( call( m, buf, 0, buf, sizeof(buf), NULL ) <= 0 ) return -1; + return 0; +} + + +int m_gstat( struct modem *modem, struct statvfs *st ) +{ + char buf[200]; + char *bufptr = buf; + + if( modem == NULL || st == NULL ) return -1; + printf( "m_gstat\n" ); + if( call( modem, "AT^SFSA=\"gstat\"\r\n", 0, buf, sizeof(buf), NULL ) <= 0 ) return -1; + + for( int i = 0; i < 2; i++ ) { + unsigned tmp; + strsep( &bufptr, "\n" ); + if( bufptr == NULL ) return -2; + switch( i ) { + case 0: + if( sscanf( bufptr, "^SFSA: %u\r\n", &tmp ) != 1) return -3; + st->f_blocks = tmp; + break; + case 1: + if( sscanf( bufptr, "^SFSA: %u\r\n", &tmp ) != 1) return -4; + st->f_bfree = tmp; + st->f_bavail = tmp; + break; + } + } + st->f_bsize = 1; + st->f_namemax = 64; + return 0; +} + +int m_init( struct modem *m, const char *tty ) +{ + char buf[100]; + if ( m == NULL ) + return -1; + if ( (m->fd = open(tty, O_RDWR | O_NOCTTY | O_SYNC)) == -1 ) { + perror( "m_init: cannot open device" ); + return -1; + } + tcgetattr( m->fd, &(m->termios) ); + cfmakeraw( &(m->termios) ); + m->termios.c_cc[VTIME] = 50; + m->termios.c_cc[VMIN] = 0; + /* set baud rates */ + cfsetispeed( &(m->termios), B115200 ); + cfsetospeed( &(m->termios), B115200 ); + /* enable the receiver and set local mode */ + m->termios.c_cflag |= (CLOCAL | CREAD); + /* set no parity, stop bits, data bits */ + m->termios.c_cflag &= ~(unsigned)PARENB; + m->termios.c_cflag &= ~(unsigned)CSTOPB; + /* set character size as 8 bits */ + m->termios.c_cflag &= ~(unsigned)CSIZE; + m->termios.c_cflag |= CS8; + /* Raw input mode, sends the raw and unprocessed data ( send as it is) */ + m->termios.c_lflag &= ~(unsigned)(ICANON | ECHO | ISIG); + /* Raw output mode, sends the raw and unprocessed data ( send as it is). */ + m->termios.c_oflag = ~(unsigned)OPOST; + tcflush( m->fd, TCOFLUSH ); + tcsetattr( m->fd, TCSANOW, &(m->termios) ); + if( call( m, "ATE0\r\n", 0, buf, sizeof(buf), NULL ) <= 0 ) + return -1; + return 0; +} + + +int m_deinit( struct modem *m ) +{ + close( m->fd ); + return 0; +} diff --git a/modem.h b/modem.h new file mode 100644 index 0000000..b040c7d --- /dev/null +++ b/modem.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include + +#define CALL_TIMEOUT 2000 +#define MAX_OPENS 24 +#define MAX_SIZE 1500 + + +enum flags { + FLAG_APPEND = 4, + FLAG_CREATE = 8, + FLAG_TRUNCATE = 16, +}; + +enum seek { + SEEK_FROMBEGIN = 0, + SEEK_FROMCURRENT = 1, + SEEK_FROMEND = 2, +}; + +enum attrs { + ATTR_FILE = 0, + ATTR_VOL = 8, + ATTR_DIR = 16 +}; + +enum results { + RESULT_SUCCESS = 0, + RESULT_NOTFOUND = 2, + +}; + +struct modem { + int fd; + struct termios termios; +}; + + +int m_close( struct modem *m, int fh ); +int m_copy( struct modem *m, const char *srcpath, const char *dstpath ); +int m_crc( struct modem *m, const char *path ); +int m_gstat( struct modem *m, struct statvfs *st ); +int m_ls( struct modem *m, const char *path, void *fusebuf, fuse_fill_dir_t fusefiller ); +int m_mkdir( struct modem *m, const char *path ); +int m_open( struct modem *m, const char *path, enum flags flags ); +int m_read( struct modem *m, int fh, unsigned size, char *outbuf ); +int m_remove( struct modem *m, const char *path ); +int m_rename( struct modem *m, const char *path, const char *newname ); +int m_rmdir( struct modem *m, const char *path ); +int m_seek( struct modem *m, int fh, int offset, enum seek flags ); +int m_stat( struct modem *m, const char *path, struct stat *fusest ); +int m_write( struct modem *m, int fh, unsigned size, const char *data ); + +int m_init( struct modem *m, const char *tty ); +int m_deinit( struct modem *m );