1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/extensions/api/braille_display_private/brlapi_connection.h" 6 7#include <errno.h> 8 9#include "base/message_loop/message_loop.h" 10#include "base/sys_info.h" 11 12namespace extensions { 13using base::MessageLoopForIO; 14namespace api { 15namespace braille_display_private { 16 17namespace { 18// Default virtual terminal. This can be overriden by setting the 19// WINDOWPATH environment variable. This is only used when not running 20// under Crhome OS (that is in aura for a Linux desktop). 21// TODO(plundblad): Find a way to detect the controlling terminal of the 22// X server. 23static const int kDefaultTtyLinux = 7; 24#if defined(OS_CHROMEOS) 25// The GUI is always running on vt1 in Chrome OS. 26static const int kDefaultTtyChromeOS = 1; 27#endif 28} // namespace 29 30class BrlapiConnectionImpl : public BrlapiConnection, 31 MessageLoopForIO::Watcher { 32 public: 33 explicit BrlapiConnectionImpl(LibBrlapiLoader* loader) : 34 libbrlapi_loader_(loader) {} 35 36 virtual ~BrlapiConnectionImpl() { 37 Disconnect(); 38 } 39 40 virtual ConnectResult Connect(const OnDataReadyCallback& on_data_ready) 41 OVERRIDE; 42 virtual void Disconnect() OVERRIDE; 43 virtual bool Connected() OVERRIDE { return handle_; } 44 virtual brlapi_error_t* BrlapiError() OVERRIDE; 45 virtual std::string BrlapiStrError() OVERRIDE; 46 virtual bool GetDisplaySize(size_t* size) OVERRIDE; 47 virtual bool WriteDots(const unsigned char* cells) OVERRIDE; 48 virtual int ReadKey(brlapi_keyCode_t* keyCode) OVERRIDE; 49 50 // MessageLoopForIO::Watcher 51 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE { 52 on_data_ready_.Run(); 53 } 54 55 virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {} 56 57 private: 58 bool CheckConnected(); 59 ConnectResult ConnectResultForError(); 60 61 LibBrlapiLoader* libbrlapi_loader_; 62 scoped_ptr<brlapi_handle_t, base::FreeDeleter> handle_; 63 MessageLoopForIO::FileDescriptorWatcher fd_controller_; 64 OnDataReadyCallback on_data_ready_; 65 66 DISALLOW_COPY_AND_ASSIGN(BrlapiConnectionImpl); 67}; 68 69BrlapiConnection::BrlapiConnection() { 70} 71 72BrlapiConnection::~BrlapiConnection() { 73} 74 75scoped_ptr<BrlapiConnection> BrlapiConnection::Create( 76 LibBrlapiLoader* loader) { 77 DCHECK(loader->loaded()); 78 return scoped_ptr<BrlapiConnection>(new BrlapiConnectionImpl(loader)); 79} 80 81BrlapiConnection::ConnectResult BrlapiConnectionImpl::Connect( 82 const OnDataReadyCallback& on_data_ready) { 83 DCHECK(!handle_); 84 handle_.reset((brlapi_handle_t*) malloc( 85 libbrlapi_loader_->brlapi_getHandleSize())); 86 int fd = libbrlapi_loader_->brlapi__openConnection(handle_.get(), NULL, NULL); 87 if (fd < 0) { 88 handle_.reset(); 89 VLOG(1) << "Error connecting to brlapi: " << BrlapiStrError(); 90 return ConnectResultForError(); 91 } 92 int path[2] = {0, 0}; 93 int pathElements = 0; 94#if defined(OS_CHROMEOS) 95 if (base::SysInfo::IsRunningOnChromeOS()) 96 path[pathElements++] = kDefaultTtyChromeOS; 97#endif 98 if (pathElements == 0 && getenv("WINDOWPATH") == NULL) 99 path[pathElements++] = kDefaultTtyLinux; 100 if (libbrlapi_loader_->brlapi__enterTtyModeWithPath( 101 handle_.get(), path, pathElements, NULL) < 0) { 102 LOG(ERROR) << "brlapi: couldn't enter tty mode: " << BrlapiStrError(); 103 Disconnect(); 104 return CONNECT_ERROR_RETRY; 105 } 106 107 size_t size; 108 if (!GetDisplaySize(&size)) { 109 // Error already logged. 110 Disconnect(); 111 return CONNECT_ERROR_RETRY; 112 } 113 114 // A display size of 0 means no display connected. We can't reliably 115 // detect when a display gets connected, so fail and let the caller 116 // retry connecting. 117 if (size == 0) { 118 VLOG(1) << "No braille display connected"; 119 Disconnect(); 120 return CONNECT_ERROR_RETRY; 121 } 122 123 const brlapi_keyCode_t extraKeys[] = { 124 BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_OFFLINE, 125 // brltty 5.1 converts dot input to Unicode characters unless we 126 // explicitly accept this command. 127 BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_PASSDOTS, 128 }; 129 if (libbrlapi_loader_->brlapi__acceptKeys( 130 handle_.get(), brlapi_rangeType_command, extraKeys, 131 arraysize(extraKeys)) < 0) { 132 LOG(ERROR) << "Couldn't acceptKeys: " << BrlapiStrError(); 133 Disconnect(); 134 return CONNECT_ERROR_RETRY; 135 } 136 137 if (!MessageLoopForIO::current()->WatchFileDescriptor( 138 fd, true, MessageLoopForIO::WATCH_READ, &fd_controller_, this)) { 139 LOG(ERROR) << "Couldn't watch file descriptor " << fd; 140 Disconnect(); 141 return CONNECT_ERROR_RETRY; 142 } 143 144 on_data_ready_ = on_data_ready; 145 146 return CONNECT_SUCCESS; 147} 148 149void BrlapiConnectionImpl::Disconnect() { 150 if (!handle_) { 151 return; 152 } 153 fd_controller_.StopWatchingFileDescriptor(); 154 libbrlapi_loader_->brlapi__closeConnection( 155 handle_.get()); 156 handle_.reset(); 157} 158 159brlapi_error_t* BrlapiConnectionImpl::BrlapiError() { 160 return libbrlapi_loader_->brlapi_error_location(); 161} 162 163std::string BrlapiConnectionImpl::BrlapiStrError() { 164 return libbrlapi_loader_->brlapi_strerror(BrlapiError()); 165} 166 167bool BrlapiConnectionImpl::GetDisplaySize(size_t* size) { 168 if (!CheckConnected()) { 169 return false; 170 } 171 unsigned int columns, rows; 172 if (libbrlapi_loader_->brlapi__getDisplaySize( 173 handle_.get(), &columns, &rows) < 0) { 174 LOG(ERROR) << "Couldn't get braille display size " << BrlapiStrError(); 175 return false; 176 } 177 *size = columns * rows; 178 return true; 179} 180 181bool BrlapiConnectionImpl::WriteDots(const unsigned char* cells) { 182 if (!CheckConnected()) 183 return false; 184 if (libbrlapi_loader_->brlapi__writeDots(handle_.get(), cells) < 0) { 185 VLOG(1) << "Couldn't write to brlapi: " << BrlapiStrError(); 186 return false; 187 } 188 return true; 189} 190 191int BrlapiConnectionImpl::ReadKey(brlapi_keyCode_t* key_code) { 192 if (!CheckConnected()) 193 return -1; 194 return libbrlapi_loader_->brlapi__readKey( 195 handle_.get(), 0 /*wait*/, key_code); 196} 197 198bool BrlapiConnectionImpl::CheckConnected() { 199 if (!handle_) { 200 BrlapiError()->brlerrno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION; 201 return false; 202 } 203 return true; 204} 205 206BrlapiConnection::ConnectResult BrlapiConnectionImpl::ConnectResultForError() { 207 const brlapi_error_t* error = BrlapiError(); 208 // For the majority of users, the socket file will never exist because 209 // the daemon is never run. Avoid retrying in this case, relying on 210 // the socket directory to change and trigger further tries if the 211 // daemon comes up later on. 212 if (error->brlerrno == BRLAPI_ERROR_LIBCERR 213 && error->libcerrno == ENOENT) { 214 return CONNECT_ERROR_NO_RETRY; 215 } 216 return CONNECT_ERROR_RETRY; 217} 218 219} // namespace braille_display_private 220} // namespace api 221} // namespace extensions 222